Next.Js — User Authentication with WSO2 Identity Server

Dimuthu Kasun
10 min readJan 20, 2022

Next.js is an open-source React web development framework built top on Node.js. NextJS framework is known for,

  • Very SEO-friendly
  • Fast time-to-market
  • Develop applications with Server-Side-Rendering, Static-Site-Generation,
    Client-Side-Rendering and Incremental-Stastical-Regeneration easily.

In this article, I will explain how to do the user authentication in a Next.js application with WSO2 Identity Server with help of the NextAuth.js library. NextAuth is an open-source authentication solution for next.js applications. you can find more details about NextAuth.js below.

In this article, I will use the abbreviation “WSO2IS” for the term “WSO2 Identity Server”.

To demonstrate NextAuth.js integration and user authentication flow with WSO2IS, I will use my existing Next.js application below.

You can find the Next.js template I have used to create the above sample application below.

This application only contains one page as the landing page and it does not have the NextAuth.js integrated. Also, I will use this for demonstration purposes and to be effective, you can use the above sample Next.js application to follow the steps in this article.

1. Create a Service Provider in the WSO2 Identity Server Management Console.

We need to create a service provider in the WSO2IS management console as it is required to initiate authentication flow with WSO2IS using authorization_code grant type.

  • Download the latest version of the identity server. Currently, the latest release is 5.11.0
  • Download the “ZIP Archive” and extract it. Go to <extracted_path>/bin/ directory and start the identity server.
./wso2server.sh
  • Access the management console application https://localhost:9443/carbon on the browser. you can log in to the application using super admin credentials.
username : admin
password : admin
  • Create a new service provider.
  • Enter a name for the service provider and click Register.
  • You will be redirected to the new service provider page.
  • Expand the “Inbound Authentication Configuration”. Then expand the “OAuth/OpenID Connect Configuration” and click Configure to add the OAuth/OpenID Connect protocol configuration to our service provider.
  • Add the below URL as the Callback Url and this is required for the NextAuth.js as the callback. More information about this callback URL can be found here.
http://{host}/api/auth/callback/wso2is
Eg: http://localhost:3000/api/auth/callback/wso2is
  • Click on Add button at the bottom of the page.
  • You will be redirected to the previous application page. you can find the client_id and client_secret from the “OAuth/OpenID Connect Configuration” section in Inbound Authentication Configurations.

2. Create New User

You can skip this step and come back after finishing all the other steps. Let’s create a new user to authenticate after we successfully finish all the steps. This can also consider as an additional step as we can use our super admin credentials to authenticate after the integration.

  • Click on Add button in the Users and Roles section in the left side pane.
  • Click on Add New User.
  • Add the information to the required fields and click Finish.

3. Allow CORS Requests

As our application is running on http://localhost:3000 WSO2IS will automatically block the API requests(SCIM /Me API request ). In order to allow CORS requests from our Next.js application, we need to add our application origin http://localhost:3000 to allowed_origins in <identity_server_extracted_path>/repository/conf/deployment.toml file.

Add the following configuration to thedeployment.toml file.

[cors]
allow_generic_http_requests = true
allow_any_origin = false
allowed_origins = [
"http://localhost:3000"
]
allow_subdomains = false
supported_methods = [
"GET",
"POST",
"HEAD",
"OPTIONS"
]
support_any_header = true
supported_headers = []
exposed_headers = []
supports_credentials = true
max_age = 3600
tag_requests = false
  • save the file and RESTART the WSO2IS.

4. Integrate NextAuth.js into our Next.js application

Hope you have set up the sample application I have mentioned earlier. As we are using the NextAuth.js library for our authentication purposes, we need to integrate it with our application.

  • Install next-auth dependency. Go to the project directory and run one of the commands below.
yarn add next-auth or npm i next-auth
  • Append the following properties to .env.local file.
  • ClientID and ClientSecret can be found from the OAuth/OpenID Connect Configuration section of the application page in the management console that we created in the first step.
  • The internal_login scope is required to invoke the SCIM /Me endpoint which I will use in this application.
  • As we didn’t create new tenants, the default tenant will be the super tenant( carbon.super ).
WSO2IS_CLIENT_ID=<client_id_of_the_appliaction>
WSO2IS_CLIENT_SECRET=<client_secret_of_the_appliaction>
WSO2IS_SCOPES=openid internal_login
WSO2IS_HOST=<https://host:port>
WSO2IS_TENANT_NAME=carbon.super
  • Create a file [...nextauth].tsx in /pages/api/auth directory. This file contains all global NextAuth.js configurations. All the requests that come to /api/auth/* (sign in, signout requests) will be handled by NextAuth.js

Providers: This is the place we need to configure all the identity providers that we going to use in our application. Of course, this is the place we need to configure WSO2IS :) .

More about custom providers can be found below.

Callbacks: We can use callbacks to control what needs to happen when some action is performed (sign in, sign out). We can use session, jwt callbacks to retrieve access_token, id_token from the Account model. Account model can use to get information about OAuth accounts associated with a user.

As you can see, in the above code snippet what I have done is attach accessToken, IdToken values with session callback which retrieved from account object in jwt callback.

If you are interested, you can find more information about callbacks, models at the below links.

  • Congrats, you have successfully integrated NextAuth.js with our Next.js application. And the most important thing is that we can do user authentication with the WSO2 Identity Server now :) .

5. Implement SignIn, SignOut, API invocation

  • Implement “Login with Identity Server” Button functionality

Open /components/header/index.tsx file and add Login button onClick event functionality as below.

import { signIn } from "next-auth/react"<Button
onClick={(e) => {
e.preventDefault()
signIn("wso2is", { callbackUrl: "/home" })
}}
primary>
Login with Identity Server
</Button>

Here, we can initiate sign-in flow by simply calling signIn method in NextAuth.js.

When calling the signIn() method with no arguments, you will be redirected to the NextAuth.js sign-in page which lists all the providers you configured. If you want to skip that and get redirected to authenticate with specific provider's immediately, call the signIn() method with the provider's id. In here id will be wso2is .

Enter the callbackUrl argument and NextAuth will automatically be redirected to that path once signIn flow is successfully completed. I have added /home path as callbackUrl . The next step would be to implement our home page.

  • Implement Home page

Create the file home.tsx in /pages directory. As you may know, Next.js has a file-system-based router built on the concept of pages. So the route for our home page will be /home .

getServerSideProps: This is the asynchronous function that we can export to enable server-side rendering in the Next.js application. Next.js will pre-render this page on each request using the data returned by getServerSideProps. Here I have returned session, host, and tenant name values as props.

  • Why use the session that is returned by getServerSideProps?
    - If you refresh the home page from a browser, and if you use usesession() hook for retrieve session, it will pop up an error. With the session returned by getServerSideProps it will always assign value for the session on every request before rendering the page.
  • why do we use the values of org and host instead of directly accessing process.env.WSO2IS_TENANT_NAME and process.env.WSO2IS_HOST from the client-side?
    - If we need to access variables on the client-side in Next.js, we need to configure them in next.config.js file. By adding the variables to next.config.js will expose the secrets to the client-side. Anyone can see these secrets. Also as the tenant name and host can vary from environment to environment, it would be better to store all the variables in one place. I chose to store them in env.* .
    The next problem is that we can’t access the variables in the env.* file from the client-side in Next.js using process.env.WSO2IS_TENANT_NAME or process.env.WSO2IS_HOST. So we need to access them from the server-side and pass them to the client-side.
  • Implement SignOut functionality.

As you can see in the home page above(/pages/home.tsx), following is the Logout button onClick event functionality.

import { signOut } from "next-auth/react"<Button
onClick={(e) => {
e.preventDefault()
signOut({ callbackUrl: "/" })
}}
primary>
Logout
</Button>

Here, we can initiate signOut flow by simply calling signOut method in NextAuth.js.

When calling the signOut() method with no arguments, definitely the session will be deleted and it won’t be redirected to any page and it will stay in the same path(`/home`). with specifying the callbackUrl, you will be redirected to the entered callback URL after the session is deleted.

  • Invoke SCIM2 /Me Endpoint

We can invoke SCIM2 API endpoints in WSO2IS using the accessToken returned after successful login.

AccessToken is stored in the Account model object and with the callbacks that I have implemented in [...nextauth].tsx , we can access them via session as below.

export default function Page({ session,org }) {const { accessToken, idToken } = session}export async function getServerSideProps(context) {
return {
props: {
session: await getSession(context), org:process.env.WSO2_TENANT_NAME
}
}
}

Now, we can invoke SCIM2 /Me endpoint with this accessToken.

const res = fetch(host+"/t/"+org+"/scim2/Me", {                method: 'get',
headers: new Headers({
"authorization": "Bearer " + accessToken
})
})
.then(r => r.json().then(data => ({
status: r.status, body: data })))
.then(res => {
console.log("API Response"+JSON.stringify(res))
})

6. Run the Application

So far, we have completed,

— Create a service provider in the WSO2IS management console
— Create a new user in Identity Server
— Configure IS to allow Next.js application CORS requests
— NextAuth.js Integration with our Next.js application
— Configure WSO2IS as a provider in NextAuth.js
— SignIn flow
— SignOut flow
— API Invocation

  • Finally, We have one remaining task. That is to run our application :).
yarn dev or npm run dev

Note: As you can see in the package.json file, "dev":"NODE_TLS_REJECT_UNAUTHORIZED='0' next" is the command that will run with any of the above commands.

  • why NODE_TLS_REJECT_UNAUTHORIZED='0' is needed for running the application?
    This is because the downloadable version of the WSO2 Identity server comes with a self-signed certificate and it causes an error in the NextAuth.js authentication flow. So I have used this command as a small hack. This is not recommended for production and you should use CA-signed certificates in WSO2IS for the production environment.
  • Then open the application by accessinghttp://localhost:3000 on the browser. Let’s log in with the user that we created earlier. (Also you will be able to login with super admin credentials) . Click Continue.
  • Give the consent to access user information from our NextJS application.
  • Then you will be able to see our home page that we implemented earlier with the decoded id token information and SCIM /Me API response.
  • If you try to log out, it will be also successful and you will be redirected to the landing page of the application.

Hope you have gained some knowledge on how to authenticate users with WSO2IS in the Next.js application using the NextAuth.js library and how to invoke the WSO2IS APIs with the accessToken with necessary permissions.

You can find the Next.js sample application with all the functionalities described above from below.

Thank you for reading this article.

Cheers!

--

--