SPA Client (Authz Code with PKCE)
Last updated
Was this helpful?
Last updated
Was this helpful?
In this third lab we want to build again an OAuth2/OIDC client for the resource server we have built in .
In contrast to and this time the client will be using the and the corresponding .
In this sixth workshop lab you will be learning how to build an OAuth 2.0/OIDC compliant frontend using Angular, that works together with the .
In contrast to this time we will see how to build a client with a browser environment without having a secure back-channel. We will use the most modern way to make this possible by facilitating the grant with .
The latest IETF working group drafts of and clearly deprecated the in favor of the + grant.
After you have completed this lab you will have learned:
that you can also use OAuth2 and OpenID Connect in a browser environment that does not provide any secure back-channel
Inside the folder lab 6 you can find 2 applications:
library-client-spa-initial: This is the client application we will use as starting point for this lab
library-client-spa-complete: This client application is the completed OAuth 2.0/OIDC client for this lab
Start by downloading the necessary dependencies. Therefore, change to the lab6/library-client-spa-initial folder using a terminal execute npm install
via command line.
Then navigate your IDE of choice (suggesting VS Code or IntelliJ) to the lab6/library-client-spa-initial project and at first explore this project a bit. Then start the application by running ng serve
on your commandline.
You will notice that the application starts up but in your browsers console you'll notice some failing HTTP requests when accessing the application. (should be 401 errors) This is because there's no authentication to your IAM solution (Keycloak).
Now stop the client application again (Ctrl-C). You can leave the resource server running as we will need this after we have finished this client.
In this step you're supposed to install the library, nothing else.
Next step is to import the library in your app.module.ts
in the imports
array.
The library we just installed gives us the ability to use the recommended authorization code grant with PKCE.
This allows us to use this improved authorization code grant without having a secure back-channel. As our application is executing in the user's browser, we are in an unsafe environment, meaning there's no secure channel the user (and by this means also an attacker) can eavesdrop.
Let's get started by creating a new service to encapsulate the authentication (and handle a few implementation quirks): ng g s services/auth --skip-tests
Now open the created file services/auth.service.ts
and initialize the configuration object:
After you've added this configuration to your service class (or as a constant to the file) you can start implementing the authentication. Start by adding instances of OAuthService and Router using dependency injection.
Now start by adding a few subjects and observables to your class. These are needed to synchronize some function calls (e.g. checking token claims should only be done once authentication is completed) and prevent race conditions.
Let's get started to set up the library in your constructor()
-function:
As you can see the configuration object we create before is put into the OAuthService. Afterwards the tokenValidationHandler
is set to NullValidationHandler
. This has one reason: The library currently has a bug which leads to being unable to disable the at_hash check with JwksValidationHandler
. Once this has been fixed, you should NOT use NullValidationHandler
.
setupAutomaticSilentRefresh()
is used to enable background refreshing of the tokens once they exceed a percentage of their maximum lifetime. (by default 75%)
Next step is to implement the method that starts the authentication process. This process is split into multiple parts:
Start the actual login procedure that redirects the user-agent to the authentication server discovered in 1. The library decides whether to use implicit or authorization code grant (with PKCE) by evaluating if responseType: 'code'
was set.
After the login has taken place, the library will automatically store the tokens and resolve the promise. After resolving the promise we are publishing that the login is done loading. In addition the query params are cleared by navigating to /
.
NOTE: Currently it's not possible to keep the state when using authorization code grant with PKCE. This is a limitation by the library and will soon be fixed.
After you implemented this function, you can use it in your AppComponent
component (app/app.component.ts
) :
As you can now already see, you are directly forced to login, and your token is later on used to query APIs. But there are a few things missing:
Your routes are not protected yet. Meaning by clever modification, users can access any part of your UI.
The name of the user logged in is not shown.
The logout button has no function at all
Let's get these bullet points fixed step-by-step.
Start by generating guard classes using the Angular CLI ng g g guards/auth
and ng g g guards/bookCreate
. If you're asked, select canActivate
. This will create two classes app/guards/auth.guard.ts
and app/guards/book-create.guard.ts
. Both should implement the CanActivate
-interface. If not, add that manually.
We'll start by modifying the routing first (without having the guards implemented). Open your app/app-routing.module.ts
and apply modifications (import necessary classes):
Now you shouldn't be able to access any component (except the header) anymore. That's correct, let's get this fixed, starting with the AuthGuard
component:
Next we'll implement the BookCreateGuard
, which will be less complicated:
You'll see, that hasRole
is missing in your AuthService
. You can try to implement it on your own by fiddling with the id token, or you simply take my set of convenience methods and add them to your app/services/auth.service.ts
:
Your routes should now be protected, but you still see the Create Book
button, even if you're not authorized to do so. Let's fix this quickly. Go to app/header/header.component.ts
and inject the AuthService
:
Now go to the template app/header/header.component.html
and add a ngIf
to the jumbotron at the bottom:
If you're not an admin or a curator, you shouldn't see the whole jumbotron anymore by now.
As you already opened the file that needs to be modified (app/header/header.component.ts
) if you followed the guide step-by-step, you can quickly add this feature. Simply fill the fullname
attribute on init:
As you can see we wait until the authService has finished processing, so we can safely access the attribute.
As in Step 4b, you'll need to modify the HeaderComponent
. Try to implement the logout()
-function yourself. π
Now it is time to see the completed application running in action.
Then start the application by running ng serve
on your commandline.
You will notice that the application starts up, and your browser will automatically redirect to our IAM solution (Keycloak) to authenticate the user.
This concludes our lab on securing a SPA with OAuth 2.0 and OpenID Connect.
how to configure an Angular application to use OAuth2.0/OIDC with authorization code + grant
Now, let's start with Lab 6. Here we will implement the required additions to get a Single-Page-Application (SPA) that calls the resource server we have implemented in . This time we will use the grant with the addition of .
We will use as identity provider. Please again make sure you have set up keycloak as described in the section.
First start the resource server application of Lab 1. If you could not complete the previous Lab yourself then use and start the completed reference application in project .
To see the application action open your browser on (as shown in the terminal after issuing ng serve
).
This will install the latest version of 's OIDC certified OAuth 2.0 / OpenID Connect library for the Angular framework. This library already supports the authorization code + PKCE flow. It can also either refresh access tokens by using silent refresh hidden frame workaround or use the real refresh tokens.
disableAtHashCheck
is currently necessary as Keycloak does not include an at_hash
claim in its id tokens. According to the this claim is optional. Keycloak will add support for this in version 11.0.0.
Kudos to , who published an example on this library which takes care of multiple race condition problems. Major parts of this service are taken directly from his example.
Get the OIDC discovery document as specified in
First make sure you still have Keycloak running, and you have started the resource server application of Lab 1 ().
To see the application action open your browser on (as shown in the terminal after issuing ng serve
).
By using the grant + instead of the we reduced risks for token leakage pretty much. The only drawback we are still facing is the fact that the library stores the tokens in browser local storage which is not a safe place in case of a cross site scripting vulnerability.
Please consult the latest draft for more details on this.