AppAuth JS integration in Single Page Application Angular React Vue

by @KDHTTPS -- Aug 17, 2020

Integrate OpenID Connect OAuth 2.0 Authorization Code PKCE Flow into your SPA Application using AppAuth JS



❤️ What is OpenID Connect and why Authorization Code flow for React(SPA) Application? 👉 Check my blog here for answer

🎯 The AppAuth JS is the best library to integrate OpenID Connect Authorization Code PKCE Flow at your React or any single page application technology.


📚 Let's Go Step by Step


Before start, You need to create an OP Client using your OP Admin Panel. Setup client, client id, client secret, redirect login URL, response_type = code, grant_type = authorization_code and refresh_token(as per your need).

https://server.com is just for example. You need to use your OP server URL.

The Best Security for Single Page Applications OpenID Connect OAuth 2.0 Authorization Code PKCE Flow

There are 3 main steps


  1. Authorization Request to OP Server
  2. Get a code from URL and get access_token
  3. Get user info using access_token

⭐ Authorization Request to OP Server


The first task is to make the authorization requests to the OpenID Connect server.

Below all code in one file. Please check my appauth-react repo for whole code.

1. First step is to initialize the RedirectRequestHandler. This object is responsible to handle the redirect task. It needs 4 Parameters.

A. Define Storage
B. URL Parameter Parser to get query params
C. Current location or URL
D. Crypto Method - to generate code_verifier and code_challenge
import {
    RedirectRequestHandler,
    LocalStorageBackend, DefaultCrypto
} from '@openid/appauth';
import { NoHashQueryStringUtils } from './noHashQueryStringUtils';

const authorizationHandler = 
    new RedirectRequestHandler(
       new LocalStorageBackend(), 
       new NoHashQueryStringUtils(), 
       window.location, 
       new DefaultCrypto()
);

2. Second step is to configure query param parser


It is for URL parsing. Default it assume that you have # in URL. If you worked on OLD Angular.js then it uses # for client-side routing.

If you want to change this method then you can easily overwrite the method like below code:

import {BasicQueryStringUtils} from '@openid/appauth';

export class NoHashQueryStringUtils extends BasicQueryStringUtils {
    parse(input, useHash) {
        return super.parse(input, false /* never use hash */);
    }
}

3. Third Step is AppAuth needs your OP Server configuration information that is provided by endpoint https://server.com/.well-known/openid-configuration.


Below AppAuthJS code help you hit, get info, and stored info in local storage. This information is internally used by AppAuthJS.

You just need to pass two parameters.

A. Your OP Server URL: for example: https://server.com
B. FetchRequester: It is Javascript Fetch API to make an HTTP Request to OP configuration endpoint. If you miss this parameter, It will use JQuery and we don't want to use JQuery in React Application.
import {
    AuthorizationServiceConfiguration,
    FetchRequestor,
} from '@openid/appauth';

AuthorizationServiceConfiguration.fetchFromIssuer([your_op_seerver_url], new FetchRequestor())
            .then((response) => {
                console.log(response)
                // You need to add auth request code here
            })
            .catch(error => {
                setError(error);
            });

4. Make an auth request. Below is a combined code with configuration info step.


 AuthorizationServiceConfiguration.fetchFromIssuer(environment.OPServer, new FetchRequestor())
            .then((response) => {
                const authRequest = new AuthorizationRequest({
                    client_id: 'your_client_id',
                    redirect_uri: 'your_redirect_login_url',
                    scope: 'openid email profile',
                    response_type: AuthorizationRequest.RESPONSE_TYPE_CODE,
                    // extras: environment.extra
                });

               // Please check next point 5 for this.
authorizationHandler.performAuthorizationRequest(response, authRequest);

            })
            .catch(error => {
                setError(error);
            });

Pass extra parameters using extra property.


5. Redirect to OP for Auth


It needs two parameters first configuration information and second is auth Request.

Use the below code for this. Once this code executes, it will redirect you to OP Server.

authorizationHandler.performAuthorizationRequest(response, authRequest);

⭐ OP Server will authenticate the user and redirect back to your side with code in URL. Let's assume you configure redirect login URL is https://client.com/callback. Please check my appauth-react repo for Flow GIF and code. You will get an idea.

⭐ Get a code from URL and get access_token

Let's assume URL in the browser is like now https://client.com/callback?code=[code_send_by_op_server]

we are now on /callback react-router. so you need to handle the next operations on this route.

Note: You can combine these steps into one file. Currently, for an easy explanation, I am doing it in different files.


1. The first step you need to configure the AuthorizationNotifier which will trigger when you want to process code(the code from URL).


import {
    AuthorizationServiceConfiguration,
    RedirectRequestHandler,
    AuthorizationNotifier,
    FetchRequestor, LocalStorageBackend, DefaultCrypto
} from '@openid/appauth';

import {NoHashQueryStringUtils} from './noHashQueryStringUtils';

const authorizationHandler = new RedirectRequestHandler(new LocalStorageBackend(), new NoHashQueryStringUtils(), window.location, new DefaultCrypto());

const notifier = new AuthorizationNotifier();
        authorizationHandler.setAuthorizationNotifier(notifier);

notifier.setAuthorizationListener((request, response, error) => {
    // response object returns code which is in URL i.e. response.code
    // request object returns code_verifier i.e request.internal.code_verifier
    // you will need to add here token request process
}

2. Above notifier only trigger when you want it using below code:


authorizationHandler.completeAuthorizationRequestIfPossible()

Once this code executes, it will trigger the notifier and in the response object, you will get code from URL.


3. Request for access_token


The below code is inside the notifier.

A. First, you need to create a token request object
B. Again get configuration information
C. Hit `/token` endpoint and get token

const tokenHandler = new BaseTokenRequestHandler(new FetchRequestor());

notifier.setAuthorizationListener((request, response, error) => {
            console.log('Authorization request complete ', request, response, error);
            if (response) {
                console.log(`Authorization Code  ${response.code}`);

                let extras = null;
                if (request && request.internal) {
                    extras = {};
                    extras.code_verifier = request.internal.code_verifier;
                }

                // A. First, you need to create a token request object
                const tokenRequest = new TokenRequest({
                    client_id: 'your_client_id',
                    redirect_uri: 'your_redirect_login_url',
                    grant_type: GRANT_TYPE_AUTHORIZATION_CODE,
                    code: response.code,
                    extras
                });

                // B. Again get configuration information
AuthorizationServiceConfiguration.fetchFromIssuer(environment.OPServer, new FetchRequestor())
                    .then((oResponse) => {
                        const configuration = oResponse;
                        // C. Hit `/token` endpoint and get token
                        return tokenHandler.performTokenRequest(configuration, tokenRequest);
                    })
                    .then((oResponse) => {
                        localStorage.setItem('access_token', oResponse.accessToken);
                        // do operation now as per your need
                        props.history.push('/profile');
                    })
                    .catch(oError => {
                        setError(oError);
                    });
            }
        });

Now, you have access_token, you can store it in localStorage and use it in the whole application.


⭐ Get user info using access_token


You don't need AppAuthJS for this task. You just need to hit the /userinfo endpoint of your OP Server and it will return you the user information.

https://server.com is just for example. You need to use your OP server URL.

let's assume we are now on /profile page.


const fetchUserInfo = async (token) => {
    const res = await fetch(`https://server.com/userinfo`, {
        headers: {
            authorization: `Bearer ${token}`
        }
    });
    return res.json();
};

export const Profile = () => {
    const [userInfo, setUserInfo] = useState(null);

    useEffect(() => {
        const fetchToken = localStorage.getItem('access_token');
        fetchUserInfo(fetchToken)
            .then((info) => {
                console.log(info);
                setUserInfo(info);
            })
        return () => {
        }
    }, []);

  return (
  .....
  );
}

Done.

We just integrated AppAuth JS into React Application.

Please check my appauth-react repo for whole integration.

If you are on angular then please check my appauth-angular repo.

You can integrate AppAuthJS in any client-side technology using the above steps.

Hope this will help !!!

#HappySharing #HappyHelping