How to implement QR Code Login Authentication Across Devices

Adeesha Savinda de Silva
9 min readAug 28, 2023

--

smart device displaying QR code
Photo by Proxyclick Visitor Management System on Unsplash

There might be a project that you are potentially engaged in that revolves around a smart device equipped with a screen, where inputting a username and password through a keyboard is difficult, has challenges, or is impossible. Or there might be a requirement that you are willing to implement QR code-based authentication. Either way, this is a process that involves sharing credentials or delegating the authentication of one device to another device that poses a camera that can read a QR code.

This article will explain how to implement QR code authentication using the OAuth 2.0 device authorization grant (RFC 8628) [1] available in the WSO2 Identity Server (IS) out of the box. The next section will briefly explain what the device authorization grant is. Refer to this link [2] for the official documentation from WSO2 on the Device Authorization flow.

Device Authorization Grant Flow

tv and remote
Photo by Towfiqu Barbhuiya

This can be explained intuitively with an example that may be familiar to you. Consider a hotel room that has a smart TV. It has an application to order food from the in-room dining menu. While lying on the cozy bed, entering the provided username and the random password by the receptionist might be a tedious process. Luckily, it will provide an option to scan a QR code displayed on the TV screen. Once this QR code is scanned with your mobile you will be directed to a page that can enter the username and password.

The underlying process is briefly explained in the diagram below. The application on the smart TV will request an Identity Provider (IDP) to authenticate the application. In the diagram, IDP is named IS because the WSO2 Identity Server(IS) is being used as an example IDP here.

In response, IDP will send a link that will redirect to a web page where credentials can be entered. This link will be converted to a QR code and displayed on the TV screen.

Device Authorization Grant Flow explained with an example

As observed in the above diagram, the third step is divided into two parts named 3-A and 3-B. These two steps are initiated parallelly. While the verification URI and the user code are converted and displayed in the QR code, the application on the device (Smart TV) will start polling for the token from the Identity Provider (IDP). Once the user enters the valid credentials and gets authenticated from the mobile phone, a valid access token will be issued to the device (Smart TV).

Stage1: Setting up the Server and Applications

Step1: Setting WSO2 Identity Server and Service Provider

For this purpose, a PC with the WSO2 Identity Server running locally, a mobile device, and a network router that both devices are connected to one single LAN is required.

Please download the WSO2 Identity Server (IS) distribution from the official website and refer to this documentation for installing locally if you are not familiar with it. WSO2 Identity Server (IS) will be considered as the Identity Provider which this article may refer to IDP as well.

Note: Make sure to change the hostname and the node_ip of the Identity Server instance from “localhost” and “127.0.0.1” to the IP address assigned to the PC that it is installed in the deployment.toml file situated in the <IS_HOME>/repository/conf directory as shown in the screenshot below.

Changing the hostname and node_ip to the IP of the PC

Configure the firewall rules to access WSO2 IS via port 9443 (or the port WSO2 IS is running. Default is 9443) from an external device if necessary.

The management console of the Identity Server (IS) can be accessed using the following URL. The default port of the Identity Server (IS) is 9443.

https://<IS-HOST>:<PORT>/carbon/admin/login.jsp
  1. Create a service provider: It is better to create a separate service provider for this purpose. Click on the “+ Add” in the Main Menu of Identity>Service Providers Section which is on the left-hand side of the user interface.
  2. Then enter “device-flow-test” (or a name you desire) as the Service Provider Name. Enter a suitable description and click “Register”.
  3. Then expand Inbound Authentication Configuration and expand OAuth/OpenID Connect Configuration and click on “Configure”.
  4. Make sure “urn:ietf:params:oauth:grant-type:device_code” is enabled in the Allowed Grant Types section and uncheck all the other grant types as they are not being used. Enable “Allow authentication without the client secret”. Change the Token Issuer to JWT to obtain a JWT as the access token. Click “Add” to save the changes.
Service Provider Configurations

5. Make note of the generated OAuth Client Key below.

Copy the generated Client Key

6. After configuring the service provider, expand Claim Configuration on the same window and change choose http://wso2.org/claims/username from the dropdown for Subject Claim URI. This will add the username of the user to the sub claim of the JWT access token and will help in displaying the username of the logged-in user. The default value for the Subject Claim URI would be the UUID of the particular user.

Change the Subject Claim URI to the username

Step 2: Configuring the Client Application

Visit this GitHub Repository [3] to download the latest release of the sample application. Go to the index.js file in the device-flow-qr-login-sample/src/config directory and provide the host and the port of the server where the WSO2 Identity Server (IS) is hosted. Copy and paste the OAuth Client Key obtained in point no.5 of Step 1 in the above section.

This application can be used as a reference for your projects. It is developed using ReactJS, Redux, and TailwindCSS and can be

An example of the configuration file is shown below.

const IDP_HOST = '10.0.0.100' // ex: localhost or 192.168.1.200 or example.com;
const IDP_PORT = '9443' // default port for WSO2 IS is 9443;

export const IDP_BASE_URL = `https://${IDP_HOST}:${IDP_PORT}`;

export const IDP_DEVICE_AUTH_URL = `https://${IDP_HOST}:${IDP_PORT}/oauth2/device_authorize`; // WSO2 IS default device authorize endpoint
export const IDP_TOKEN_URL = `https://${IDP_HOST}:${IDP_PORT}/oauth2/token`; // WSO2 IS default token endpoint

export const CLIENT_ID = 'myVerySecretClientKey'; //CLIENT_ID of the Application

export const GRANT_TYPE = 'urn:ietf:params:oauth:grant-type:device_code';

Run the following commands to quickly spin up the sample application.

npm install

and then

npm start

To run the application in development mode. Visit http://localhost:3000 on a browser to experience the application. A UI similar to below should appear on the browser window if everything is configured correctly.

Landing Page of the sample application

Stage 2: Testing QR Code Authentication

It is time to play around and test things out once everything is configured properly.

Two devices connected to a LAN are required for this scenario to be tested. Once again make sure that WSO2 IS is accessible through the firewall of the hosted PC.

Option1: Testing with the sample application

Open up the sample application on a browser window on the first device by visiting the URL (eg: http://localhost:3000).

  1. Click the Login button.
  2. A QR code will be displayed.
QR Code is displayed

3. Scan the QR code displayed in the application from the second device. Click on Reset if you want to generate a new user code.

4. It will redirect to a page with the user code pre-entered. Click Continue. (At this point you may be provided with a message mentioning that the connection is not private. Click on Advance and proceed).

5. Enter the username and password and click Continue.

Scanning, Entering User Code, and Authenticating from mobile

6. A success message with the logged-in username will be displayed in the application.

Successful login

Explanation

  1. Once the login button is clicked on the application of the first device, it will send an authorization request to the Identity Provider (IDP) in this case WSO2 IS.
  2. IDP will send a response with verification_uri, user_code, and device_code.
  3. The application will then convert this verification_uri to a QR code and will display it.
  4. Once this is scanned from the second device, a browser window is opened and redirected to a login page.
  5. The user code is auto-filled here. If not it can be found in the application UI and can be entered there.
  6. Then the username and password can be entered for the user to be authenticated.
  7. While scanning and entering username and password happens on the second device, the first device where the application is hosted will keep on polling the IDP for Access Token.
  8. The token is received at the first device Once the user is authenticated.
  9. The username obtained by decoding this token is displayed on the application with a success message.

Testing with REST APIs

If you want to understand the behavior of the device authorization flow properly it is always better to test it out with the REST APIs. You can follow the cURL commands in order to test out the device authorization grant. Also, you can find the Postman collection from this link.

First, send the request to the device_authorize endpoint.

curl -k -X POST -H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=<CLIENT_ID>' \
https://<HOST>:<PORT>/oauth2/device_authorize

On success, you will receive a response as follows.

{
"user_code": "<USER CODE>",
"device_code": "<DEVICE CODE>",
"interval": 5,
"verification_uri_complete": "https://localhost:9443/authenticationendpoint/device.do?user_code=<USER CODE>",
"verification_uri": "https://localhost:9443/authenticationendpoint/device.do",
"expires_in": 600
}

Copy and paste the verification_uri_complete or verification_uri on a browser window. verification_uri_complete will autofill your user code in the window. Alternatively, you may be able to use a free online QR code generator to convert this URL into a QR code.

Click Continue to proceed. Then enter the username and password of the user to authenticate.

After authentication is successful, use the following cURL command to request the token from the token endpoint. Copy and paste the device code obtained from the previous response.

curl -k -X POST -H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=urn:ietf:params:oauth:grant-type:device_code' \
--data-urlencode 'client_id=<CLIENT_ID>' \
--data-urlencode 'device_code=<DEVICE_CODE>' \
https://<HOST>:<PORT>/oauth2/token

On success, you will receive a response as follows.

{
"access_token": "<ACCESS TOKEN>",
"token_type": "Bearer",
"expires_in": 3600
}

Hint: If you have tried to obtain the access token before authentication, you will receive a response as follows.

{
"error_description": "Precondition required",
"error": "authorization_pending"
}

Advance Configurations

If you are willing to make more changes in the key set used to generate the user code, length of the user code, device and user code expiration time, and polling interval please add the following configurations to the deployment.toml file which can be found in <IS_HOME>/repository/conf folder. <IS_HOME> is the root folder where WSO2 IS is installed. Once changes are done, restart the server to affect the changes.

[oauth.grant_type.device_code]
key_length = 7
expiry_time = "10m"
polling_interval = "5s"
key_set = "BCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz23456789"

The default values are shown above. My preference for the user code would be the following as it is more human-readable.

  • key_set = “123456789”
  • key_length = 8

References

[1] https://www.rfc-editor.org/rfc/rfc8628
[2] https://is.docs.wso2.com/en/latest/references/concepts/authorization/device-flow-grant/
[3] https://github.com/adeesha-savinda/device-flow-qr-login-sample.git
[4] https://is.docs.wso2.com/en/latest/guides/access-delegation/try-device-flow/

--

--

Adeesha Savinda de Silva

Associate Lead Solutions Engineer @WSO2 | IAM Expert | M.Eng. (Hons) Electronic Engineering