ES Module OpenID Connect Implicit Grant Client
A small client implementing the OpenID Connect Implicit Grant authentication flow with no dependencies.
This was created because a number of other OpenID clients support all the authentication options for OpenID Connect, so you end up shipping all that code with your client.
This is a basic, small, lightweight client that does the following
-
Generates the OpenID authorisation URL, with a nonce and state object that can be used for implicit grant
-
Decodes the response and returns it
The library does not implement any state management. You need to implement this yourself.
The library implements validate of the state
and nonce
of the request and id_token
return from the OpenID provider.
This library does not validate the JWT returned using Implicit Grant. It is recommended this is done server side by whatever service your app is using.
Basic usage
npm install @esmodule/openid-implicit-grant
Implicit grant with OpenID Connect requires calling a /authorize
URL with various options specified. The client creates this URL, along with a state
and nonce
parameter to ensure uniqueness of the response.
Generate URL
import { login } from '@esmodule/openid-implicit-grant'
const options = {
domain: '* Domain name for your OIDC tenant, without http:// or /authorize *',
client_id: '* OIDC Client Id *',
redirect_uri: 'http://localhost:1234/callback.html', //the callback URL the OIDC connect provider redirects to.
scope: 'openid profile' //OpenID scope
}
export default function authRedirect () {
// return the OIDC authorization URL needed to authenticate the client, along with the state and nonce needed later to validate the response
const { url, validation } = login(options)
// put the validation object somewhere you can retrieve it from later, this example uses sessionStorage, but could be your frameworks state mangement API
window.sessionStorage.setItem('validation', JSON.stringify(validation))
return url
}
Decode returned JWT in the callback
import { callback } from '@esmodule/openid-implicit-grant'
// hash string in the funciton below is the string after the # in the url returned by the OIDC provider. It must not include #.
function decodeToken (hash) {
// retrieve the validation object from where you stashed it
const validation = JSON.stringify(window.sessionStorage.getItem('validation'))
const { idToken, accessToken } = callback(hash, validation)
return { idToken, accessToken }
}
Example
Configure your OpenID Connect provider (Auth0, Okta, AWS Cognito, Azure Active Directory, etc) as per the relevant instructions for Implicit Grant. Set the redirect_uri
to http://localhost:1234/callback.html
in the OpenID Connect provider setup.
Then create the following pages.
Login page
login.html
<!DOCTYPE html>
<html lang="en">
<title></title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link
rel="stylesheet"
href="https://unpkg.com/tachyons/css/tachyons.min.css"
/>
<body>
<div class="w-100 vh-100 dt v-mid">
<div class="dtc v-mid tc">
<input
id="login_button"
type="button"
value="Login"
class="pointer grow ph4 pv3 no-underline ba br2 b--transparent bg-navy white f4 avenir"
/>
</div>
</div>
</body>
<script src="login.js" type="module"></script>
</html>
login.js
Update options
object in the JS below with the details from your OpenID connect provider
import { login } from '@esmodule/openid-implicit-grant'
const options = {
domain: '* Domain name for your OIDC tenant, without http:// or /authorize *',
client_id: '* OIDC Client Id *',
redirect_uri: 'http://localhost:1234/callback.html',
scope: 'openid profile'
}
const loginButton = document.getElementById('login_button')
loginButton.addEventListener('click', authRedirect)
function authRedirect () {
const reqString = genURL(options)
location.href = reqString
}
Callback page
This is the page that your OIDC provider will redirect to.
callback.html
<!DOCTYPE html>
<html lang="en">
<title></title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link
rel="stylesheet"
href="https://unpkg.com/tachyons/css/tachyons.min.css"
/>
<body>
<div class="w-100 vh-100 dt v-mid">
<div class="dtc v-mid tc">
<span id="login_status" class="gray f3 code"></span>
</div>
</div>
</body>
<script src="callback.js" type="module"></script>
</html>
callback.js
import { callback } from '@esmodule/openid-implicit-grant'
const returnHash = window.location.href.split('#')[1] || ''
const validation = JSON.stringify(window.sessionStorage.getItem('validation'))
const { idToken, accessToken } = login(returnHash, validation)
const loginStatus = document.getElementById('login_status')
if (returnHash) {
if (idToken) {
loginStatus.innerHTML = `Successful login for ${idToken.claims.preferred_username}`
console.log(idToken.claims) //write the claims to the console to show contents
} else {
loginStatus.innerHTML = 'Failed login'
}
} else {
loginStatus.innerHTML = 'No access token provided. Unable to log you in.'
}
Dev Server
We're going to use parcel for a local dev server, as it will bundle the @esmodule/openid-implicit-grant
package for us. You can obviously use Webpack or Rollup for bundling.
Install parcel as per the instructions, then run the following command
If parcel is installed globally
parcel *.html
If parcel is installed locally
npx parcel *.html
This should bundle the html pages with the javascript and start a dev server at http://localhost:1234
. Go to http://localhost:1234/login.html
to get the login page. Clicking the Login
button with start the OIDC Implicit Grant flow.
Once logged in, you can check the localStorage
to see that id_token
has been written there, for future use by your application.
This is a basic JS based example. The library is lightweight and can be used in any popular framework easily.