react-oauth2-auth-code-flow

Simplifying authorization via OAuth2's Authorization Code Flow (and PKCE) via React Components

Usage no npm install needed!

<script type="module">
  import reactOauth2AuthCodeFlow from 'https://cdn.skypack.dev/react-oauth2-auth-code-flow';
</script>

README

React OAuth2 Auth Code Flow

Simplifying authorization via OAuth2's Authorization Code Flow (and PKCE) via React Components

What

react-oauth2-auth-code-flow is a library of components to simplify the use of OAuth2's Authorization Code Grant specifically within [react] applications in the context of Innoactive's Portal services.

This package builds upon the excellent react-oauth2-auth-code-flow components to:

  1. generate the necessary link to send users to the correct location to grant authorization
  2. obtain an access token once the user is back on your site

and uses client-oauth2 under the hood, thereby combining two very well-designed libraries.

This library also supports the PKCE extension, which is recommended for SPAs, out of the box.

Installation

using npm:

npm install --save react-oauth2-auth-code-flow

using yarn:

yarn add react-oauth2-auth-code-flow

This package currently depends on having a module bundler in place, i.e. there's no browser compatible version (umd) available.

Usage

react-oauth2-auth-code-flow exports two Components:

<RequestAuthorizationCode />

import React, { Component } from "react";
import { RequestAuthorizationCode } from "react-oauth2-auth-code-flow";
import ClientOAuth2 from "client-oauth2";

const oauthClient = new ClientOAuth2({
  clientId: process.env.CLIENT_ID,
  clientSecret: process.env.CLIENT_SECRET,
  accessTokenUri: `${apiRoot}/oauth/token/`,
  authorizationUri: "https://www.dropbox.com/oauth2/authorize",
  redirectUri: "https://www.yourapp.com/auth/dropbox",
  scopes: ["read"],
});

export default class SendToDropbox extends Component {
  render() {
    return (
      <RequestAuthorizationCode
        oauthClient={oauthClient}
        state={{ from: "/settings" }}
        render={({ url }) => <a href={url}>Connect to Dropbox</a>}
      />
    );
  }
}

Use <RequestAuthorizationCode /> to send your users to the correct endpoints at your OAuth2 service.

Props

Prop Type Required Default Description
oauthClient ClientOAuth2 yes - An instance of the ClientOAuth2 library
state object no - Additional state to get back from the service provider (read more below)
args object no - Additional args to send to service provider, e.g. scope. Will be serialized by qs

Render

<RequestAuthorizationCode /> can be used in two ways, either by a render-prop, or component-prop. In either way they will recieve the generated url as a prop/arg.

const RenderProp = (props) => (
  <RequestAuthorizationCode
    {...props}
    render={({ url }) => <a href={url}>Connect</a>}
  />
);

const Link = ({ url }) => <a href={url}>Connect</a>;
const ComponentProp = (props) => (
  <RequestAuthorizationCode {...props} component={Link} />
);

State

You can pass some state along with the auth process. This state will be sent back by the OAuth-provider once the process is done. This state can for example then be used to redirect the user back to where they started the auth process.

<AuthorizationCodeCallback />

import React, { Component } from "react";
import { AuthorizationCodeCallback } from "react-oauth2-auth-code-flow";
import ClientOAuth2 from "client-oauth2";

const oauthClient = new ClientOAuth2({
  clientId: process.env.CLIENT_ID,
  clientSecret: process.env.CLIENT_SECRET,
  accessTokenUri: `${apiRoot}/oauth/token/`,
  authorizationUri: "https://www.dropbox.com/oauth2/authorize",
  redirectUri: "https://www.yourapp.com/auth/dropbox",
  scopes: ["read"],
});

export default class ReceiveFromDropbox extends Component {
  handleSuccess = async (accessToken, { response, state }) => {
    console.log("Successfully authorized");
    await setProfileFromDropbox(accessToken);
    await redirect(state.from);
  };

  handleError = (error) => {
    console.error("An error occurred");
    console.error(error.message);
  };

  render() {
    return (
      <AuthorizationCodeCallback
        oauthClient={oauthClient}
        onAuthSuccess={this.handleSuccess}
        onAuthError={this.handleError}
        render={({ processing, state, error }) => (
          <div>
            {processing && <p>Authorizing now...</p>}
            {error && (
              <p className="error">An error occurred: {error.message}</p>
            )}
          </div>
        )}
      />
    );
  }
}

Use <AuthorizationCodeCallback /> to handle authorization when the user is being redirected from the OAuth2-provider.

Props

Prop Type Required Default Description
oauthClient ClientOAuth2 yes - An instance of the ClientOAuth2 library
args object no - Args will be attatched to the request to the token endpoint. Will be serialized by qz
location { search: string } no - Used to extract info from querystring (read more below)
querystring string no - Used to extract info from querystring (read more below)
tokenFetchArgs object no {} Used to fetch the token endpoint (read more below)
tokenFn func no null Used to bypass default fetch function to fetch the token (read more below)

Events

  • onAuthSuccess(accessToken, result)
Arg Type Description
accessToken string Access token recieved from OAuth2 provider
result object
result.response object The full token response from the call to the token-endpoint
result.state object The state recieved from provider, if it was provided earlier
  • onAuthError(error)
Arg Type Description
error Error Error with message as description of what happened

Render

<AuthorizationCodeCallback /> can be used in two ways, either by a render-prop or component-prop. Either way they will recieve three props/args:

  • processing: boolean: True if authorization is in progress
  • state: object: The state received from provider (might be null)
  • error: Error: An error object if an error occurred
const RenderProp = (props) => (
  <AuthorizationCodeCallback
    {...props}
    render={({ processing, state, error }) => (
      <div>
        {processing && <p>Authorization in progress</p>}
        {state && <p>Will redirect you to {state.from}</p>}
        {error && <p className="error">Error: {error.message}</p>}
      </div>
    )}
  />
);

const View = ({ processing, state, error }) => (
  <div>
    {processing && <p>Authorization in progress</p>}
    {state && <p>Will redirect you to {state.from}</p>}
    {error && <p className="error">Error: {error.message}</p>}
  </div>
);
const ComponentProp = (props) => (
  <AuthorizationCodeCallback {...props} component={View} />
);

location and querystring

--> see react-oauth-flow docs

tokenFetchArgs

--> see react-oauth-flow docs

tokenFn

--> see react-oauth-flow docs

Acknowledgements