manfredsteyer / angular-oauth2-oidc

Support for OAuth 2 and OpenId Connect (OIDC) in Angular.
MIT License
1.89k stars 687 forks source link

Create sample that shows how to integrate with Azure Active Directory ( AAD ) #494

Open mores opened 5 years ago

mores commented 5 years ago

It is known that Azure Active Directory endpoints do not allow Cross Origin Resourece Sharing (CORS).

Others have documented it is a PITA to integrate with Azure Active https://github.com/manfredsteyer/angular-oauth2-oidc/issues/135

Others got frustrated about Azure AD not respecting OIDC, so they switched to using their own library directly https://github.com/manfredsteyer/angular-oauth2-oidc/issues/126

mores commented 5 years ago

I have a sample that works with google sso. I then preceded to replace all google urls and keys with those from Azure. I currently have this isssue: SEC7120: [CORS] The origin 'https://demo:4200' did not find 'https://demo:4200' in the Access-Control-Allow-Origin response header for cross-origin resource at 'https://login.microsoftonline.com/XXXXXXXXXX/discovery/v2.0/keys'.

But it looks like the return url does contain a valid id_token.

manfredsteyer commented 5 years ago

We will write something down regarding ADFS and Azure AD. For the time beeing, you might find the following snippet from a real-world project useful.

All those things can also be configured within an config-object passed to .configure(...).

this.oauthService.redirectUri = environment.oauthRedirectUri;
this.oauthService.clientId = 'client-id';
this.oauthService.responseType = 'code token id_token';
this.oauthService.scope = 'openid allatclaims';
this.oauthService.logoutUrl = 'https://adfs-test.de/adfs/oauth2/logout';
this.oauthService.issuer = 'https://adfs-test.de/adfs';

/* This needs to be set manually b/c ADFS and Azure AD do not support CORS */
this.oauthService.resource = 'urn:example:example';
this.oauthService.userinfoEndpoint = 'https://adfs-test.de/adfs/userinfo';
this.oauthService.loginUrl = 'https://adfs-test.de/adfs/oauth2/authorize/';
this.oauthService.tokenEndpoint = 'https://adfs-test.de/adfs/oauth2/token/';

this.oauthService.events.pipe(filter(e => e.type === 'token_received')).subscribe(_ =>
{
   // do this and that ...
});

this.oauthService.tokenValidationHandler = new NullValidationHandler();

Another thing to consider is that you have to configure two apps/resources in your Azure AD: The client (Angular app) and the Backend.

folego commented 5 years ago

Hi manfredsteyer,

Thank you for the library. I used to create an Ionic App and worked very well. Now I am trying to do the same with Azure AD, using the v1 or v2. Doing some research, I notice that Azure doesn't allow call and get the discovery document because they don't have the CORS enabled (like @mores comment before).

Do you have success to write an example using AAD? I am struggling to understand the different URLs that Azure provides in the configuration and how to use them using your last sample code posted here.

In my app, I am getting a valid token and sending to the API, and there I am validating using the .Net Core JWT authentication.

services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options =>
   {
      options.Authority = "https://dev-262704.oktapreview.com/oauth2/default";
      options.Audience = "api://default";
   });
mores commented 5 years ago

I think that this is needed: this.oauthService.strictDiscoveryDocumentValidation = false; other wise you get discovery_document_validation_error

mores commented 5 years ago

When I tried the scope per your comment, azure is reporting: The scope openid allatclaims is not valid so I am using this.oauthService.scope = 'openid';

mores commented 5 years ago

"oauth2AllowImplicitFlow": true, should be in the Manifest of the registered app inside of Azure Active Directory admin center

mores commented 5 years ago

Here is my extremely simple Login button for Azure: https://github.com/mores/angular-examples/tree/master/oidc-azure

I update src/app/app.component.ts with my guid tenant - and my client id

After hitting the login button, the url that shows in my browser is in the form of https://demo:4200/#access_token=eyJ0eXAiOiJ< snip >Vio68Xw &token_type=Bearer&expires_in=14399&scope=email+openid+profile&id_token=eyJ0eXAiOiJ< snip >IrXM4i1J8Q&state=Pxz< snip >a08&session_state=5e798974-< snip >-cdf9dcb4c3a0

I have gotten back valid tokens from Azure.

Console shows: error loading jwks angular-oauth2-oidc.js:521

has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

Any help is appreciated.

jeroenheijmans commented 5 years ago

If you get a CORS error, is there also a failed request in the network tab of your dev tools? What host is it trying to reach?

If you could also add this gist's code way up early in your code, then you'd also get some additional logging you could share, so we can know what bit of logic in the library is causing the CORS request.

mores commented 5 years ago

I do not see any errors in the network tab.

There are two OAuthErrorEvent type: "jwks_load_error" statusText: "Unknown Error"

type: "discovery_document_load_error" statusText: "Unknown Error"

It is having issues with this url: https://login.microsoftonline.com/ < enter guid here>/discovery/v2.0/keys

mores commented 5 years ago

I now understand what @manfredsteyer was trying to say in his comment above.... any and all communication is proxied through adfs-test.de. ( middleware ) adfs-test.de is a private server needed to facilitate the communication from angular to azure.

mores commented 5 years ago

Other people are having issues validating token too: https://github.com/Microsoft/azure-spring-boot/issues/476

I am able to verify the idToken but not the accessToken. Looking to see if Microsoft can shed any light here: https://github.com/MicrosoftDocs/azure-docs/issues/28355

JanSvejda commented 5 years ago

Currently, I made it work with Azure B2C with the following setup:

import { Injectable } from '@angular/core';
import { OAuthService } from 'angular-oauth2-oidc';
import { MyValidationHandler } from './validation.handler';

@Injectable({
  providedIn: 'root'
})
export class AuthService {

  constructor(private oauthService: OAuthService, private validationHandler: MyValidationHandler) {
  }

  getIdentityClaims() {
    return this.oauthService.getIdentityClaims();
  }
  logout() {
    this.oauthService.logOut();
  }
  login() {
    this.oauthService.initImplicitFlow();
  }

  setup() {
    this.oauthService.configure({
      responseType: 'token id_token',
      logoutUrl: 'http://localhost:8080/',
      issuer: 'https://<name>.b2clogin.com/<name>.onmicrosoft.com/v2.0/',
      strictDiscoveryDocumentValidation: false,
      tokenEndpoint: 'https://<name>.b2clogin.com/<name>.onmicrosoft.com/oauth2/v2.0/token?p=signinsignup',
      loginUrl: 'https://devcvp.b2clogin.com/devcvp.onmicrosoft.com/oauth2/v2.0/authorize?p=b2c_1_signinsignup',
      redirectUri: 'http://localhost:8080/login/oauth2/code/dsa-azure-b2c',
      clientId: '<client-id>',
      dummyClientSecret: '<secret>',
      scope: 'openid offline_access ....',
      skipIssuerCheck: true,
      oidc: true,
    });
    this.validationHandler.setKeyUri('https://<name>.b2clogin.com/<name>.onmicrosoft.com/discovery/v2.0/keys?p=signinsignup');
    this.oauthService.tokenValidationHandler = this.validationHandler;
    this.oauthService.setStorage(sessionStorage);

    this.oauthService.tryLogin({});
  }
}

I also had to use the PR #527 from @ismcagdas. Otherwise the flow fails.

Furthermore, to validate the tokens, I adapted the JwkValidationHandler to first fetch the keys from a configured URL, though nothing complicated.

Hope this helps.

mores commented 5 years ago

I think I found out what "special processing" means: https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/issues/609#issuecomment-405683736 ( Azure is adding a ‘nonce’ to the header - The JWS is signed with a SHA2 of the nonce, the ‘nonce’ is replaced before the JWS is serialized. ) It sounds like there is a need for a AzureValidationHandler to validate the access token.

mores commented 5 years ago

I think I have a good working example now: https://github.com/mores/angular-examples/tree/master/oidc-azure This ONLY utilizes an idToken, which will validate from a subclass of JwksValidationHandler.

Matt-J-Hosking commented 5 years ago

They seem to have enabled CORS for their jwks endpoint now. Best to switch over or get caught out like we did with a cached copy of the old keys.

PrashantShrimal commented 5 years ago

I am totally new in OIDC and need to use OIDC with Azure active directory. Does any one have a working example where no Identity server is required. I tried Mores's example but it also need a 'PRIVATE_PROXY_SERVER'.

webdeveloperninja commented 5 years ago

Where did you get the issuer url in the portal?

Matt-J-Hosking commented 5 years ago

Where did you get the issuer url in the portal?

If you open 'https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration' (the standard OIDC discovery endpoint) you get all the JSON data for their configuration. Replace 'common' with your Azure tenant ID for more specific values.

ccruza commented 5 years ago

I think I have a good working example now: https://github.com/mores/angular-examples/tree/master/oidc-azure This ONLY utilizes an idToken, which will validate from a subclass of JwksValidationHandler.

Hello @mores I followed your example. I did not understand this part (app.component.ts) :

this.oauthService.loadDiscoveryDocument( 'https://' + PRIVATE_PROXY_SERVER + '/angular2azure/openid-configuration?tenant=' + TENANT_GUID ).then( doc => {......

So, I change it for:

this.oauthService.loadDiscoveryDocument('https://login.microsoftonline.com/thisismydirectoryname.onmicrosoft.com/v2.0/.well-known/openid-configuration').then( doc => {....

And It started to work, so now everything is fine.. I'm going to adapt this to my actual project..

Thanks a lot!

mjisaak commented 5 years ago

I wrote a blog post about how to use the component with Azure AD B2C: https://about-azure.com/2019/09/26/using-azure-ad-b2c-with-angular-8/ The post includes a live demo and the source code. Hope that helps you.

gerbermichi commented 4 years ago

Does anyone have experience with the userinfo endpoint? The following code leads to a 401 - "Access token validation failure. Invalid audience." oauthService.loadUserProfile().then(u => console.log(u));

eastjie commented 3 years ago

Does anyone implement in Azure AD (not Azure B2C) with PCKE successfully? Thanks.