Angular OIDC OAuth2 client with Google Identity Platform

This article shows how an Angular client could implement a login for a SPA application using Google Identity Platform OpenID. The Angular application uses the npm package angular-auth-oidc-client to implement the OpenID Connect Implicit Flow to connect with the google identity platform.

Code: https://github.com/damienbod/angular-auth-oidc-sample-google-openid

Setting up Google Identity Platform

The Google Identity Platform provides good documentation on how to set up its OpenID Connect implementation.

You need to login into google using a gmail account.
https://accounts.google.com

Now open the OpenID Connect google documentation page

https://developers.google.com/identity/protocols/OpenIDConnect

Open the credentials page provided as a link.

https://console.developers.google.com/apis/credentials

Create new credentials for your application, select OAuth Client ID in the drop down:

Select a web application and configure the parameters to match your client application URLs.

Implementing the Angular OpenID Connect client

The client application is implemtented using ASP.NET Core and Angular.

The npm package angular-auth-oidc-client is used to connect to the OpenID server. The package can be added to the package.json file in the dependencies.

"dependencies": {
    ...
    "angular-auth-oidc-client": "0.0.8"
},

Now the AuthModule, OidcSecurityService, AuthConfiguration can be imported. The AuthModule.forRoot() is used and added to the root module imports, the OidcSecurityService is added to the providers and the AuthConfiguration is the configuration class which is used to set up the OpenID Connect Implicit Flow.


import { AuthModule, OidcSecurityService, AuthConfiguration } from 'angular-auth-oidc-client';

@NgModule({
    imports: [
        BrowserModule,
        FormsModule,
        routing,
        HttpModule,
        JsonpModule,
        AuthModule.forRoot(),
    ],
    declarations: [
        AppComponent,
        ForbiddenComponent,
        HomeComponent,
        UnauthorizedComponent
    ],
    providers: [
        OidcSecurityService,
        Configuration
    ],
    bootstrap:    [AppComponent],
})

The AuthConfiguration class is used to configure the module.

stsServer
This is the URL where the STS server is located. We use https://accounts.google.com in this example.

redirect_url
This is the redirect_url which was configured on the google client ID on the server.

client_id
The client_id must match the Client ID for Web application which was configured on the google server.

response_type
This must be ‘id_token token’ or ‘id_token’. If you want to use the user service, or access data using using APIs, you must use the ‘id_token token’ configuration. This is the OpenID Connect Implicit Flow. The possible values are defined in the well known configuration URL from the OpenID Connect server.

scope
Scope which are used by the client. The openid must be defined: ‘openid email profile’

post_logout_redirect_uri
Url after a server logout if using the end session API. This is not supported by google OpenID.

start_checksession
Checks the session using OpenID session management. Not supported by google OpenID

silent_renew
Renews the client tokens, once the token_id expires.

startup_route
Angular route after a successful login.

forbidden_route
HTTP 403

unauthorized_route
HTTP 401

log_console_warning_active
Logs all module warnings to the console.

log_console_debug_active
Logs all module debug messages to the console.


export class AppModule {
    constructor(public authConfiguration: AuthConfiguration) {
        this.authConfiguration.stsServer = 'https://accounts.google.com';
        this.authConfiguration.redirect_url = 'https://localhost:44386';
        // The Client MUST validate that the aud (audience) Claim contains its client_id value registered at the Issuer identified by the iss (issuer) Claim as an audience.
        // The ID Token MUST be rejected if the ID Token does not list the Client as a valid audience, or if it contains additional audiences not trusted by the Client.
        this.authConfiguration.client_id = '188968487735-b1hh7k87nkkh6vv84548sinju2kpr7gn.apps.googleusercontent.com';
        this.authConfiguration.response_type = 'id_token token';
        this.authConfiguration.scope = 'openid email profile';
        this.authConfiguration.post_logout_redirect_uri = 'https://localhost:44386/Unauthorized';
        this.authConfiguration.start_checksession = false;
        this.authConfiguration.silent_renew = true;
        this.authConfiguration.startup_route = '/home';
        // HTTP 403
        this.authConfiguration.forbidden_route = '/Forbidden';
        // HTTP 401
        this.authConfiguration.unauthorized_route = '/Unauthorized';
        this.authConfiguration.log_console_warning_active = true;
        this.authConfiguration.log_console_debug_active = true;
        // id_token C8: The iat Claim can be used to reject tokens that were issued too far away from the current time,
        // limiting the amount of time that nonces need to be stored to prevent attacks.The acceptable range is Client specific.
        this.authConfiguration.max_id_token_iat_offset_allowed_in_seconds = 3;
        this.authConfiguration.override_well_known_configuration = true;
        this.authConfiguration.override_well_known_configuration_url = 'https://localhost:44386/wellknownconfiguration.json';
    }
}

Google OpenID does not support the .well-known/openid-configuration API as defined by OpenID. Google blocks this when using it due to a CORS security restriction, so it can not be used from a browser application. As a workaround, the well known configuration can be configured locally when using angular-auth-oidc-client. The goole OpenID configuration can be downloaded using the following URL:

https://accounts.google.com/.well-known/openid-configuration

The json file can then be downloaded and saved locally on your server and this can then be configured in the authConfiguration class using the override_well_known_configuration_url property.

this.authConfiguration.override_well_known_configuration = true;
this.authConfiguration.override_well_known_configuration_url = 'https://localhost:44386/wellknownconfiguration.json';

The following json is the actual configuration for the google well known configuration. What’s really interesting is that the end session endpoint is not supported, which is strange I think.
It’s also interesting to see that the response_types_supported supports a type which is not supported “token id_token”, this should be “id_token token”.

See: http://openid.net/specs/openid-connect-core-1_0.html

{
  "issuer": "https://accounts.google.com",
  "authorization_endpoint": "https://accounts.google.com/o/oauth2/v2/auth",
  "token_endpoint": "https://www.googleapis.com/oauth2/v4/token",
  "userinfo_endpoint": "https://www.googleapis.com/oauth2/v3/userinfo",
  "revocation_endpoint": "https://accounts.google.com/o/oauth2/revoke",
  "jwks_uri": "https://www.googleapis.com/oauth2/v3/certs",
  "response_types_supported": [
    "code",
    "token",
    "id_token",
    "code token",
    "code id_token",
    "token id_token",
    "code token id_token",
    "none"
  ],
  "subject_types_supported": [
    "public"
  ],
  "id_token_signing_alg_values_supported": [
    "RS256"
  ],
  "scopes_supported": [
    "openid",
    "email",
    "profile"
  ],
  "token_endpoint_auth_methods_supported": [
    "client_secret_post",
    "client_secret_basic"
  ],
  "claims_supported": [
    "aud",
    "email",
    "email_verified",
    "exp",
    "family_name",
    "given_name",
    "iat",
    "iss",
    "locale",
    "name",
    "picture",
    "sub"
  ],
  "code_challenge_methods_supported": [
    "plain",
    "S256"
  ]
}

The AppComponent implements the authorize and the authorizedCallback functions from the OidcSecurityService provider.

import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { Configuration } from './app.constants';
import { OidcSecurityService } from 'angular-auth-oidc-client';
import { ForbiddenComponent } from './forbidden/forbidden.component';
import { HomeComponent } from './home/home.component';
import { UnauthorizedComponent } from './unauthorized/unauthorized.component';


import './app.component.css';

@Component({
    selector: 'my-app',
    templateUrl: 'app.component.html'
})

export class AppComponent implements OnInit {

    constructor(public securityService: OidcSecurityService) {
    }

    ngOnInit() {
        if (window.location.hash) {
            this.securityService.authorizedCallback();
        }
    }

    login() {
        console.log('start login');
        this.securityService.authorize();
    }

    refreshSession() {
        console.log('start refreshSession');
        this.securityService.authorize();
    }

    logout() {
        console.log('start logoff');
        this.securityService.logoff();
    }
}

Running the application

Start the application using IIS Express in Visual Studio 2017. This starts with https://localhost:44386 which is configured in the launch settings file. If you use a differnt URL, you need to change this in the client application and also the servers client credentials configuration.

Login then with your gmail.

And you are redirected back to the SPA.

Links:

https://www.npmjs.com/package/angular-auth-oidc-client

https://developers.google.com/identity/protocols/OpenIDConnect

Advertisements

2 comments

  1. Damien, I noticed that you say Google block the downloading of the configuration of the OIDC configuration from the .well-known/openid-configuration endpoint, and therefore the downloading of it, and saving of it to a local file.

    For providers that does support this, will your library automatically download the configuration from the .well-known/openid-configuration endpoint – ie. we do not have to download it and save it locally first?

    1. Hi Jerrie, at present, I do this manually and add it to the using project. It is not part of the npm lib. The lib allows you to configure the src if required.

      Google blocks it for a browser application; CORS. You can download it from a desktop application or direct in the browser.
      Greetings Damien

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: