Writing a Custom OAuth2 Grant Type in WSO2 Identity Server

Dhaura Pathirana
8 min readMar 30, 2022

OAuth stands for Open Authorization and it is a lightweight protocol used for secure access delegation. It is a token-based access delegation method where a third-party application does not need the username and password of a user to access the user’s resources. Currently, OAuth 2.0 is the widely used version of OAuth.

OAuth2 Grant Types

There are several ways or methods that can be used to implement the above-mentioned token-based authorization technique and these methods are known as “grant types”. Four major standard grant types are supported in OAuth2 authorization servers and the WSO2 Identity Server has the flexibility to support any custom grant type as well. To learn more about these standard grant types, refer to the following blog.

Implementing a Custom Grant Type

In order to implement a custom grant type, you need to write a handler and a validator for your grant type.

  1. Grant Type Handler — Specifies how the validation must be done and how the token should be issued. This can be done in two ways. Either you can implement the “AuthorizationGrantHandler” Interface or you can extend the “AbstractAuthorizationGrantHandler” class. In this blog, the implementation will be done by extending the “AbstractAuthorizationGrantHandler” class.
  2. Grant Type Validator — Verifies and validates the token request and checks whether all the required parameters are sent with the request. This can be implemented by extending the “AbstarctValidator” class.

In this blog, for demonstration purposes, a simple custom grant type called “Mobile Grant” will be implemented. Here, you will pass a mobile number to the OAuth2 “token” endpoint in exchange for an access token. You can find the GitHub repository for the implementation, here.

First, a Maven Project should be created and in the source folder (a folder named “Java”), a package (org.wso2.carbon.identity.custom.grant) will be created for the new grant type. In this package, two java classes should be created. One for the validator and the other for the handler. For clarifications, the following image will illustrate the folder structure. [For relevant dependencies and imported packages, you can refer to the pom.xml file in the GitHub repository.]

Folder Structure for Mobile Grant

Now let’s move on to coding inside the java classes. First, let’s go through the handler.

In the MobileGrantHandler, the “AbstractAuthorizationGrantHandler” class will be extended as mentioned before. After that, the “validateGrant” method of the superclass will be overridden, so that, the validation for the new custom grant can be customized according to our needs.

@Override
public boolean validateGrant(OAuthTokenReqMessageContext tokReqMsgCtx) throws IdentityOAuth2Exception {

}

This method will take an object of “OAuthTokenReqMessageContext” as a parameter that contains all the information that we send through the request.

Inside the method, we’ll first call the super “validateGrant” method to go through the default validation process and after that, we can start implementing our own validation.

boolean validateGrant = super.validateGrant(tokReqMsgCtx);

If the result from the “super.validateGrant” is true, that means the default validation is successful. Then, we can move on to our validation process.

In the token request, there will be three mandatory request parameters.

  1. Grant Type — Specifies the type of the grant. (In this case, it’ll be “mobile_grant”)
  2. Mobile Number — Number that is being used for the token request.
  3. ID Token — It will be used to retrieve the user ID of the requester. (You can obtain an ID Token for a specific user by doing a Password Grant token request to the Identity Server with a scope of “openid”)

Next, the mobile number and the ID token will be extracted from the request parameters.

RequestParameter[] parameters = tokReqMsgCtx.getOauth2AccessTokenReqDTO().getRequestParameters();

String mobileNumber = null;
String userID = null;
String idTokenHint = null;

for (RequestParameter parameter : parameters) {
if (MOBILE_NUMBER.equals(parameter.getKey())) {
if (parameter.getValue() != null && parameter.getValue().length > 0) {
mobileNumber = parameter.getValue()[0];
}
}
if (ID_TOKEN_HINT.equals(parameter.getKey())) {
if (parameter.getValue() != null && parameter.getValue().length > 0) {
idTokenHint = parameter.getValue()[0];
}
}
}

Here, MOBILE_NUMBER and ID_TOKEN_HINT are some constants that will be initialized at the beginning of the class.

public static final String MOBILE_NUMBER = "mobile_number";
public static final String ID_TOKEN_HINT = "id_token_hint";

After that, a new method for mobile number validation will be declared.

private boolean isValidMobileNumber(String mobileNumber){

// just demo validation

if(mobileNumber.startsWith("011")){
return true;
}

return false;
}

This will be a private method since it will be only used inside the class. This is just a demo validation and you can customize this method as you need. Next, in the “validateGrant” method, it will be checked if the parameter values are not null and if the mobile number is valid according to the implemented validation method.

As the next step, an Authorized user will be created and the user ID will be extracted from the ID token. Other user information will be hardcoded here for simplicity. (You can use the extracted user ID to obtain other user info but it would be a little bit complex for the purpose of this blog.) Another point that should be noted here is that you can use your own way to get this Authorized user according to the requirements of your custom grant type.

if (mobileNumber != null && idTokenHint != null && isValidMobileNumber(mobileNumber)){
AuthenticatedUser mobileUser = new AuthenticatedUser();
try {
userID = SignedJWT.parse(idTokenHint).getJWTClaimsSet()
.getSubject();
} catch (ParseException e) {
if (log.isDebugEnabled()) {
log.debug("Error occurred while retrieving user ID from ID token", e);
}
}
mobileUser.setUserId(userID);
mobileUser.setUserName(mobileNumber);
mobileUser.setAuthenticatedSubjectIdentifier(mobileNumber);
mobileUser.setFederatedUser(false);
mobileUser.setTenantDomain("carbon.super");
mobileUser.setUserStoreDomain("PRIMARY");
tokReqMsgCtx.setAuthorizedUser(mobileUser);
tokReqMsgCtx.setScope(tokReqMsgCtx.getOauth2AccessTokenReqDTO().getScope());

return true;
}

In the end, the created authorized user will be inserted into the token request message context (tokReqMsgCtx) object. After that, if there are any scopes, they will also be inserted into the above object. If the whole process goes well, the “validateGrant” method will return true. (The whole code for “MobileGrantValidator” can be found in the previously mentioned GitHub repository.)

Therefore, we can move on to the validator implementation.

In the MobileGrantValidotor, you should extend the “AbstarctValidator” class as mentioned before. In the constructor, you should mention what parameters in the request body should be mandatory.

package org.wso2.carbon.identity.custom.grant;

import org.apache.oltu.oauth2.common.validators.AbstractValidator;

public class MobileGrantValidator extends AbstractValidator {

public MobileGrantValidator() {
requiredParams.add(MobileGrantHandler.MOBILE_NUMBER);
requiredParams.add(MobileGrantHandler.ID_TOKEN_HINT);
}
}

Now, as the coding part has been concluded, we can do a Maven build with the project. First, open up a terminal and navigate into the project’s root folder and enter the following command.

mvn clean install -DskipTests

If the build is successful, a folder named “target” will be created and inside it, you’ll find a .jar file for the mobile grant.

Built .jar file for Mobile Grant

After that, you can transfer (copy and paste) the above .jar file into IS_HOME/repository/components/lib folder. Here, IS_HOME is the root directory of the WSO2 Identity Server. (To download and install the Identity Server, follow this link.)

Now, open the IS_HOME/repository/conf/deployment.toml file and add the following lines at the end of the file.

[[oauth.custom_grant_type]]
name="mobile_grant"
grant_handler="org.wso2.carbon.identity.custom.grant.MobileGrantHandler"
grant_validator="org.wso2.carbon.identity.custom.grant.MobileGrantValidator"
[oauth.custom_grant_type.properties]
IdTokenAllowed=true

After saving the above deployment.toml file, run (or restart if already running) the Identity server.

As the next step, a service provider application has to be created in the Identity Server and it can be done through the Management Console or just by executing the following curl command. (“mobile_grant” should be specified as an allowed grant type for the application.)

curl -k -X POST -H "Authorization: Basic YWRtaW46YWRtaW4=" -H "Content-Type: application/json" -d '{  "client_name": "application_test", "grant_types": ["password","mobile_grant"], "redirect_uris":["http://localhost:8080/playground2"] }' "https://localhost:9443/api/identity/oauth2/dcr/v1.1/register"

After the execution, you will get a response similar to the following.

{"client_name":"application_test","client_id":"vyMSzVoLS0ZUnY3F2gHkhytEdnka","client_secret":"Z4MYJLQyhCfrVE9mZBCRfO0C6Msa","redirect_uris":["http://localhost:8080/playground2"]}

Now, copy the client id and secret and get the base64 encoded value for client_id:client_secret. (Base64 encoded value can be obtained here.) This value will be used in the Authorization header to authenticate the token request.

To obtain an ID token, run the following command. (It’s a password grant token creation request with OpenID as a scope.)

curl -k -X POST https://localhost:9443/oauth2/token -H 'Authorization: Basic <base64encoded(client_id:client_secret)>' -H 'Content-Type: application/x-www-form-urlencoded' -d 'grant_type=password&username=<username>&password=<password>&scope=openid'

A sample request for the admin user is mentioned below.

curl -k -X POST https://localhost:9443/oauth2/token -H 'Authorization: Basic dnlNU3pWb0xTMFpVblkzRjJnSGtoeXRFZG5rYTpaNE1ZSkxReWhDZnJWRTltWkJDUmZPMEM2TXNh' -H 'Content-Type: application/x-www-form-urlencoded' -d 'grant_type=password&username=admin&password=admin&scope=openid'

In the response, you will receive an ID token and copy it for future use. (It will be used when writing a mobile grant token creation request.)

Now, the grant type is ready for a test run. Let’s create a Postman request for mobile grant token generation because Postman makes it easier to understand the request and its properties.

URL for the request:

https://localhost:9443/oauth2/token

Headers for the request:

Under the “Authorization” key, the value should be replaced with the base64 encoded value that you’ve obtained before.

Body of the request:

Here, the grant_type is specified as mobile_grant and a sample valid mobile number is supplied. There is only one scope is specified here but you can have a set of scopes here by space-separated. For the id_token_hint, replace the value with the ID token that you copied before.

Now you can send the request and the response will be similar to the following.

If you are able to get a response similar to the above response, then the custom grant type has been successfully implemented.

Thus, we have reached the end of the blog, and hopefully, it would have been helpful for you to get started on creating a custom grant type with the WSO2 Identity Server.

--

--