All you need to run your APIs
On the load of the application , if there was a previous valid rotating refresh token issue to the SPA, the SDK will check for a valid refresh token. As a fallback mechanism the SDK does a silent authentication, if no refresh token exists, and only if all these steps fail, a complete redirect happens to the IdP to check for SSO sessions.
In order to have a better user experience by avoiding multiple unnecessary calls to the IdP, client applications can add a query parameter prompt=none to the authorize endpoint, only when there is an active user in that application and when the client application is unable to re- hydrate the cache with an existing rotating refresh token.
The guides below demonstrates how to integrate your Single Page Application with the Identity Product, on our development, testing, staging and production environments.
Before we are able to create your application and share the needed details you need to provide:
Callback url
Is a URL in your application that you would like the Identity Product to
redirect users to after they have authenticated. If not set, users will not be
returned to your application after they log in. Example: for local development
you can use http://localhost:4200
.
Logout url
Is a URL in your application that you would like the Identity Product to
redirect users to after they have logged out, by using returnTo query
parameter. If not set, users will not be able to log out from your application
and will receive an error. Example: for local development you can use
http://localhost:4200
.
Allowed Web Origins
If you don't add your application URL on Allowed Web Origins, the application
will be unable to silently refresh the authentication tokens and your users
will be logged out the next time they visit the application, or refresh the
page. Example: for local development you can use http://localhost:4200
.
Others:
After sharing the needed details (above) we will provide, per environment:
Your application should install our preferred package, auth0/auth0-angular
.
Install the Angular SDK by executing the following commands on your terminal:
npm install @auth0/auth0-angular
The SDK exposes several types that help you integrate with your Angular application idiomatically, including a module and an authentication service.
The SDK exports AuthModule
, a module that contains all the services required
for the SDK to function. To register this with your application:
app.module.ts
fileAuthModule
type from the @auth0/auth0-angular
packageAuthModule
to the application by calling AuthModule.forRoot
and adding
to your application module's imports
arrayimport { BrowserModule } from '@angular/platform-browser'
import { NgModule } from '@angular/core'
import { AppComponent } from './app.component'
// Import the module from the SDK
import { AuthModule } from '@auth0/auth0-angular'
@NgModule({
declarations: [AppComponent],
imports: [
BrowserModule,
// Import the module into the application, with configuration
AuthModule.forRoot({
domain: 'identity.iagl.digital',
clientId: 'zgZSVCjshfhHhVOKAzBPl8pt5qhFGqHS',
}),
],
bootstrap: [AppComponent],
})
export class AppModule {}
The pattern forRoot()
is used to configure the module, which takes the
properties domain
and clientId
; the values of these properties correspond to
the "Domain" and "Client ID" values that were shared before.
The Angular SDK gives you tools to quickly implement user authentication in your
Angular application, such as creating a login button using the
loginWithRedirect()
method from the AuthService
service class. Exeug
loginWithRedirect()
redirects your users to the Identity Product Login Page,
where users can be authenticated. Upon successful authentication, Identity
Product will redirect users back to your application.
// Import the AuthService type from the SDK
import { AuthService } from '@auth0/auth0-angular';
@Component({
selector: 'app-auth-button',
template: '<button (click)="auth.loginWithRedirect()">Log in</button>'
})
export class AuthButtonComponent {
// Inject the authentication service into your component through the constructor
constructor(public auth: AuthService) {}
}
Now that you can log in to your Angular application, you need a way to log out.
You can create a logout button using the logout()
method from the AuthService
service. Executing logout()
redirects your users to your Auth0 logout endpoint
and then immediately redirects them to your application.
Here is a modified version of the AuthButtonComponent component above that uses
both loginWithRedirect()
and logout()
, as well as checking the
authentication state using the isAuthenticated
observable:
import { Component, Inject } from '@angular/core';
import { AuthService } from '@auth0/auth0-angular';
import { DOCUMENT } from '@angular/common';
@Component({
selector: 'app-auth-button',
template: \`
<ng-container *ngIf="auth.isAuthenticated$ | async; else loggedOut">
<button (click)="auth.logout({ returnTo: document.location.origin })">
Log out
</button>
</ng-container>
<ng-template #loggedOut>
<button (click)="auth.loginWithRedirect()">Log in</button>
</ng-template> \`,
styles: [],
})
export class AuthButtonComponent {
constructor(@Inject(DOCUMENT) public document: Document, public auth: AuthService) {}
} {}
The user
observable contains sensitive information and artifacts related to
the user's identity. As such, its availability depends on the user's
authentication status. Fortunately, the user
observable is configured so that
it only starts to emit values once the isAuthenticated
observable is true, so
there is no need to manually check the authentication state before accessing the
user profile data.
Before we are able to create your application and share the needed details you need to provide:
Callback url
Is a URL in your application that you would like the Identity Product to
redirect users to after they have authenticated. If not set, users will not be
returned to your application after they log in. Example: for local development
you can use http://localhost:3000
.
Logout url
Is a URL in your application that you would like the Identity Product to
redirect users to after they have logged out, by using returnTo query
parameter. If not set, users will not be able to log out from your application
and will receive an error. Example: for local development you can use
http://localhost:3000
.
Allowed Web Origins
If you don't add your application URL on Allowed Web Origins, the application
will be unable to silently refresh the authentication tokens and your users
will be logged out the next time they visit the application, or refresh the
page. Example: for local development you can use http://localhost:3000
.
Others:
After sharing the needed details (above) we will provide, per environment:
Your application should install our preferred package, auth0/auth0-spa-js
.
Install the SPA SDK by executing the following commands on your terminal:
npm install @auth0/auth0-spa-js
Once the Auth0 SPA SDK is installed, reference it using an import statement at the entrypoint of your application:
import createAuth0Client from '@auth0/auth0-spa-js'
Alternatively, if you do not use a package manager such as Webpack, you can retrieve the SPA SDK from CDN.
<script src='https://cdn.auth0.com/js/auth0-spa-js/1.20/auth0-spa-js.production.js'></script>
This guide will use it to provide a way for your users to log in to your JavaScript application.
When a user logs in, three items are returned:
access_token
id_token
expires_in
: the number of seconds before the Access Token expiresYou can use these items in your application to set up and manage authentication.
Create a basic HTML page
Create a folder on your machine to house the application, then add an
index.html
file to the root of the project. This HTML page will display a
welcome message and have a "gated" section which requires the user to be
authenticated before accessing. You can copy/paste the following content into
the file. You will be adding more lines as you progress with this article.
Add the following content to the index.html
file you just created:
import { Component, Inject } from '@angular/core';
import { AuthService } from '@auth0/auth0-angular';
import { DOCUMENT } from '@angular/common';
@Component({
selector: 'app-auth-button',
template: \`
<ng-container *ngIf="auth.isAuthenticated$ | async; else loggedOut">
<button (click)="auth.logout({ returnTo: document.location.origin })">
Log out
</button>
</ng-container>
<ng-template #loggedOut>
<button (click)="auth.loginWithRedirect()">Log in</button>
</ng-template> \`,
styles: [],
})
export class AuthButtonComponent {
constructor(@Inject(DOCUMENT) public document: Document, public auth: AuthService) {}
} {}
Open the newly-created public/css/main.css
file and add the following CSS:
.hidden {
display: none;
}
label {
margin-bottom: 10px;
display: block;
}
Finally, create a new directory in the public folder
called js
, and a new
file in there called app.js.
This will house the application- specific logic
that you will create over the next few sections.
The folder structure so far should look like the following:
.
├── index.html
└── public
├── css
│ └── main.css
└── js
└── app.js
This article is based on the new SPA SDK available here. You can reference the
package from the CDN in the index.html
file by placing the script tags at the
very bottom of the body tag:
<body>
<!-- other HTML -->
<!-- add the lines below existing code -->
<script src="https://cdn.auth0.com/js/auth0-spa-js/1.20/auth0-spa-js.production.js"></script>
<script src="js/app.js"></script>
</body>
Create an auth_config.json
in the root of the project. The values from
domain
and clientId
were shared previously.
{
"domain": "identity.iagl.digital",
"clientId": "zgZSVCjshfhHhVOKAzBPl8pt5qhFGqHS"
}
auth_config.json
is served publicly, this file should never contain sensitive information such as passwords and client secrets.
Install and set up your preferred application framework.
The SDK must be properly initialized with the information of the application created above.
To start, open the public/js/app.js
file and add a variable to hold the client
object:
let spaClient = null
This must be initialised using the values from the auth_config.json
file. This
can be done by calling the endpoint on the server that was created in the
previous section. To do this, create a new function calledfetchAuthConfig
further down the app.js
file, which can be used to download this information:
// ..
const fetchAuthConfig = () => fetch('/auth_config.json')
Next, create another new function called configureClient. This will use
fetchAuthConfig
to download the configuration file and initialize the
spaClient
variable:
// ..
const configureClient = async () => {
const response = await fetchAuthConfig()
const config = await response.json()
spaClient = await createAuth0Client({
domain: config.domain,
client_id: config.clientId,
})
}
This call will also populate the in-memory cache with a valid access token and user profile information if someone has already authenticated before and that session is still valid.
Add a handler for the window.onload
function that will then make this call to
initialise the application:
// ..
window.onload = async () => {
await configureClient()
}
Now go and access http://localhost:3000
. You should see the welcome message
and both authentication buttons disabled. Note however that some browsers cache
the page sources. When checking each step results you should perform a full page
refresh ignoring the cache. This can be achieved by using CMD+SHIFT+R
keys on
OSX CTRL+SHIFT+R
keys on Windows.
As a first approach, you want to make sure anyone is able to visit the public
page but not the page that is meant for authenticated users only, such as a
settings panel or the user profile details. You can decide which content is
available by hiding, disabling, or removing it if no user is currently logged
in. You do so by checking the result of calling the
spaClient.isAuthenticated()
method. Use this to enable or disable Log nd Log
out buttons, which are disabled by default. This can be part of aw updateUI()
function called from the window.onload
method right after the initialisation.
Still inside the app.js
file, add a new function called updateUI
and modify
onload handler to call this new function:
// ..
window.onload = async () => {
await configureClient()
// NEW - update the UI state
updateUI()
}
// NEW
const updateUI = async () => {
const isAuthenticated = await spaClient.isAuthenticated()
document.getElementById('btn-logout').disabled = !isAuthenticated
document.getElementById('btn-login').disabled = isAuthenticated
}
Authentication is achieved through a redirect to the Identity Product login. Once the user logs in, the result will be passed to your app's redirect URI, which is provided with the authorisation request.
Inside the app.js
file, provide login
function that calls
spaClient.loginWithRedirect()
to perform the login step. The login function is
called ye Log in button previously defined in the HTML page. In this sample, you
will redirect the user back to the same page they are now. You can obtain that
value from window.location.origin
property:
// ..
const login = async () => {
await spaClient.loginWithRedirect({
redirect_uri: window.location.origin,
})
}
Additionally, because thi a single page application, the result of this call needs to be handled in the same context. This means that when the page is loaded and the user is not authenticated you could be in one of the following two scenarios:
This second scenario is the one you need to handle. In your window.onload
method, check whether the user is authenticated or not, and if the URL query
contains both a code
and state
parameter. This will indicate that an
authentication result is present and needs to be parsed. In that scenario, you
do so by calling spaClient.handleRedirectCallback()
method. This will attempt
to exchange the result that the Identity Product backend gave you back for real
tokens you can use.
In addition, the query parameters must be removed from the URL so that if the
user refreshes the page, the app does not try to parse state
and code
parameters again. This is achieved with the window.history.replaceState
method.
Modify the window.onload
function ne app.js
to include these changes:
// .. code committed for brevity
updateUI();
const isAuthenticated = await spaClient.isAuthenticated();
if (isAuthenticated) {
// show the gated content
return;
}
// NEW - check for the code and state parameters
const query = window.location.search;
if (query.includes("code=") && query.includes("state=")) {
// Process the login state
await spaClient.handleRedirectCallback();
updateUI();
// Use replaceState to redirect the user away and remove the query string parameters
window.history.replaceState({}, document.title, "/");
}
};
// ..
The callback is now handled properly and the authentication can be completed successfully.
Run the project and click the on button. You should be taken to the Identity Product login page. Go ahead and log in. After authenticating successfully, you will be redirected to the page you were before. This time, the result will be present in the URL query and the exchange will happen automatically. If everything went fine, you will end up with no query parameters in the URL, the user would now be logged in and the "Log out" button will be enabled.
You may have noticed that the Log out button is clickable when the user is authenticated, but does nothing. You need to add the code that will log the user out from the Identity Product backend.
Start the log out by calling spaClient.logout()
method passing a valid
return-to URI. In this sample you will return the user back to the same page
they are now. You can obtain that value from window.location.origin property.
Abstract this logic into a logout() method.
// public/js/app.js
const logout = () => {
auth0.logout({
returnTo: window.location.origin,
})
}
If you see any errors from the Identity Product server, check that you have not forgotten to register the logout URL as explained initially.
Every time a user is logged in you get access both to the access token
and
ID token
. The user's profile information is then extracted from the ID token.
Typically, the token is used to call your backend application and the profile
information is used to display their name and profile picture. In this section
you are going to display them in separate text areas so you can easily inspect
them.
Open the index.html file and insert the following lines at the bottom of the body.
<body>
<!-- ... -->
<div class="hidden" id="gated-content">
<p>
You're seeing this content because you're currently
<strong>logged in</strong>.
</p>
<label>
Access token:
<pre id="ipt-access-token"></pre>
</label>
<label>
User profile:
<pre id="ipt-user-profile"></pre>
</label>
</div>
<!-- .. existing script tags .. -->
</body>
Now re-open the app.js file and modify the updateUI() function declared previously. Add the logic such that when the user is logged in the gated content is shown. Use the existing variables and functions from the SDK client to obtain and display this information on the page. In addition, at the start of this article you added a public/css/main.css file with the definition of the hidden class, which can be used to easily hide elements on the page. Using the authenticated flag as shown below, add or remove this class to the elements you want to show or hide in the updateUI() function:
// ...
const updateUI = async () => {
const isAuthenticated = await spaClient.isAuthenticated()
document.getElementById('btn-logout').disabled = !isAuthenticated
document.getElementById('btn-login').disabled = isAuthenticated
// NEW - add logic to show/hide gated content after authentication
if (isAuthenticated) {
document.getElementById('gated-content').classList.remove('hidden')
document.getElementById('ipt-access-token').innerHTML =
await spaClient.getTokenSilently()
document.getElementById('ipt-user-profile').textContent = JSON.stringify(
await spaClient.getUser()
)
} else {
document.getElementById('gated-content').classList.add('hidden')
}
}
// ..
Note that calls to the SDK instance can throw an exception if the authentication fails, if there is no user currently authenticated, or if the access token needs to be refreshed and that request fails. You will need to put a try/catch block around them to correctly handle any errors. These error checks are not shown on the article but they are available on the final sample app that you can download.
Before we are able to create your application and share the needed details you need to provide:
Callback url
Is a URL in your application that you would like the Identity Product to
redirect users to after they have authenticated. If not set, users will not be
returned to your application after they log in. Example: for local development
you can use http://localhost:3000
.
Logout url
Is a URL in your application that you would like the Identity Product to
redirect users to after they have logged out, by using returnTo query
parameter. If not set, users will not be able to log out from your application
and will receive an error. Example: for local development you can use
http://localhost:3000
.
Allowed Web Origins
If you don't add your application URL on Allowed Web Origins, the application
will be unable to silently refresh the authentication tokens and your users
will be logged out the next time they visit the application, or refresh the
page. Example: for local development you can use http://localhost:3000
.
Others:
After sharing the needed details (above) we will provide, per environment:
Your application should install our preferred package, auth0/auth0-react
.
Install the React SDK by executing the following commands on your terminal:
npm install @auth0/auth0-react
The SDK exposes methods and variables that help you integrate with your React application idiomatically using React Hooks or Higher-Order Components.
Under the hood, the React SDK uses
React Context to manage the
authentication state of your users. One way to integrate with your React app is
to wrap your root component with an Auth0Provider
that you can import from the
SDK.
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import { Auth0Provider } from "@auth0/auth0-react";
ReactDOM.render(
<Auth0Provider
domain="identity.iagl.digital"
clientId="zgZSVCjshfhHhVOKAzBPl8pt5qhFGqHS"
redirectUri={window.location.origin}
>
<App />
</Auth0Provider>,
document.getElementById("root")
The Auth0Provider
component takes the following props:
domain
and clientId
: The values of these properties correspond to the
"Domain" and "Client ID" values shared previously.redirectUri
The URL to where you'd like to redirect your users after they
authenticate with Identity Product.The React SDK gives you tools to quickly implement user authentication in your
React application, such as creating a login button using the
loginWithRedirect()
method from the useAuth0()
hook. Executing
loginWithRedirect()
redirects your users to the Identity Product login page,
where users can authenticate. Upon successful authentication, Identity Product
will redirect your users back to your application.
import React from 'react'
import { useAuth0 } from '@auth0/auth0-react'
const LoginButton = () => {
const { loginWithRedirect } = useAuth0()
return <button onClick={() => loginWithRedirect()}>Log In</button>
}
export default LoginButton
Now that you can log in to your React application, you need a way to log out.
You can create a logout button using the logout()
method from the useAuth0()
hook. Executing logout()
redirects your users to your logout()
endpoint and
then immediately redirects them to your application.
import React from 'react'
import { useAuth0 } from '@auth0/auth0-react'
const LogoutButton = () => {
const { logout } = useAuth0()
return (
<button onClick={() => logout({ returnTo: window.location.origin })}>
Log Out
</button>
)
}
export default LogoutButton
The React SDK helps you retrieve the profile information associated with
logged-in users quickly in whatever component you need, such as their name or
profile picture, to personalise the user
interface. The profile information is
available through the user property exposed by the useAuth0()
hook. Take this
Profile
component as an example of how to use it:
import React from 'react'
import { useAuth0 } from '@auth0/auth0-react'
const Profile = () => {
const { user, isAuthenticated, isLoading } = useAuth0()
if (isLoading) {
return <div>Loading ...</div>
}
return (
isAuthenticated && (
<div>
<img src={user.picture} alt={user.name} />
<h2>{user.name}</h2>
<p>{user.email}</p>
</div>
)
)
}
export default Profile
The user
property contains sensitive information and artifacts related to the
user's identity. As such, its availability depends on the user's
authentication status. To prevent any render errors, use the isAuthenticated
property from useAuth0()
to check if the user has authenticated before React
renders any component that consumes the user
property. Ensure that the SDK has
completed loading before accessing the isAuthenticated
property, by checking
that isLoading
is false
.
TBA