DEV Community

Cover image for AppAuth JS integration in React
Kiran Mali
Kiran Mali

Posted on

AppAuth JS integration in React

❤️ 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.

🔥 Here is React JS code with AppAuth JS integration.

GitHub logo kdhttps / appauth-react

Appauth JS integration with React 🎯

App Auth React

App-Auth JS integration with the React App. This project was bootstrapped with Create React App.

react-flow

Prerequisites

  1. Node JS >= 10.x.x
  2. Auth0 client - Currently for I am using auth0.com as a OP Server. Demo should work with every OP Provider.

Configuration

Use environment.js to set OP Client configuration.

Start

  1. Install Dependencies
npm install
  1. Start Application
npm start

Runs the app in the development mode.
Open http://localhost:4200 to view it in the browser.




📚 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.

Appauth-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

Top comments (6)

Collapse
 
lmarioni profile image
lmarioni

Hello! good article!

I followed your instrunctions and it works correctly!! But now I have to add a clientSecret propery, and I can't find how to do it...

Where is the client secret configured?

Thanks!

Collapse
 
kdhttps profile image
Kiran Mali • Edited

Hello, glad you like it.

This is PKCE Flow. You don't need Client Secret at all and also it is not safe to keep Client Secret at browser. checkout my another blog for details dev.to/kdhttps/the-best-security-f...

Collapse
 
mkarinca profile image
Murat Karınca

Hello Kiran, thanks for the article, it is a very good guidance for using PKCE flow on SPA. I know that client secret is not recommened and safe for PKCE flow but my OP server is expecting that for registered clients. How can i integrate clientSecret to this library and request token accoedingly? Could you please share the necessary coding? Thanks in advance for your support.

Collapse
 
carduscarv profile image
Cardus Crvil

hello, thanks for making this great article xD

i have one question, how about the log out mechanism?? i try to remove access_token local storage and null the userInfo, but when i hit login back, it just login with no need to auth. how to re auth after click the button logout, thanks before.. it really help

Collapse
 
kdhttps profile image
Kiran Mali

Hello, glad you like it.

you need to perform the logout manually. please check details here curity.io/resources/learn/openid-c...

Collapse
 
thiba_nt profile image
thiba nt

Hi Kiran, thanks for sharing React OpenID working example.
And I have 2 questions :
1) How to access refresh_token
2) How to access refreshed id_token, access_token, and refresh_token
Would you able to share more on above.
Thanks again, have a nice day