How to add a custom grant type in Keycloak

3,159 views
Skip to first unread message

lidijaldo

unread,
May 11, 2020, 5:13:11 AM5/11/20
to Keycloak User
Hi, 

I didn't find any documentation on implementing a custom grant type in Keycloak. 
How can this be done?

Thank you and kind regards,
Lidija

Phil Fleischer

unread,
May 11, 2020, 11:09:34 AM5/11/20
to lidijaldo, Keycloak User
I thought grants were a super set of “roles/scopes/mappers” so if you turn on the client “consent” piece then for any specific role, scope, or mapper then they would show up as a line item on the grant page.

So perhaps review the “consent required” sections of the client documentation?

Consent Required

If this is on, then users will get a consent page which asks the user if they grant access to that application. It will also display the metadata that the client is interested in so that the user knows exactly what information the client is getting access to. If you’ve ever done a social login to Google, you’ll often see a similar page. Keycloak provides the same functionality.


--
You received this message because you are subscribed to the Google Groups "Keycloak User" group.
To unsubscribe from this group and stop receiving emails from it, send an email to keycloak-use...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/keycloak-user/43dc2cf9-fea4-446f-ab9e-4420d8416a4d%40googlegroups.com.

lidijaldo

unread,
May 11, 2020, 1:41:22 PM5/11/20
to Keycloak User
Thank you for your response.

Actually, I need a custom grant_type.

There are different grant_type values that can be specified when creating REST requests to /auth and they are: 
  • authorization_code: exchange authorization code for an access token

  • refresh_token: exchange refresh token for access token when access token expires

  • password: exchange user credentials for access token

  • client_credentials: obtain an access token outside of the user context (access resources about themselves rather than user’s resources)

  • urn:ietf:params:oauth:grant-type:token-exchange: using set of credentials or token to obtain an entirely different token

  • urn:ietf:params:oauth:grant-type:uma-ticket: authorization request to obtain permissions from project

Depending on the grant_type different parameters have to be specified.

What we  would like to do is: 
1. Create a custom REST endpoint that would accept the following body parameters:
--data-urlencode 'grant_type=<GRANT_TYPE>' \ --data-urlencode 'client_id=dev' \ --data-urlencode 'first_name=John' \ --data-urlencode 'last_name=Doe' \ --data-urlencode 'orgId=someOrgId' \ --data-urlencode 'client_secret=<SECRET>'

2. GRANT_TYPE in the above request would be something that is custom because we don't have a token in this request.

3. Keycloak would check the client id and secret and if valid, it would perform a call to an external service of ours. If this call would be successfull, it would just return status OK and if not, it would return error. 

This is really only a custom logic and I know this has nothing to do with OAuth specification but it's what we currently need in our flow. 

We already have a custom endpoint implemented. The thing we're missing now is how to validate client credentials.

This is our sample code in our custom class that extends TokenEndpoint:
 
@Override
@POST
public Response processGrantRequest() {
MultivaluedMap<String, String> formParams = this.request.getDecodedFormParameters();
formParams = this.request.getDecodedFormParameters();
String grantType = (String)formParams.getFirst("grant_type");

if (! grantType.equals("register")) {
return super.processGrantRequest();
} else {
logger.info("Registering");

// TODO check client!
// checkClient();

// custom logic
return register();
}
}

private Response register() {
    // TODO add custom logic here...

Cors cors = Cors.add(request).auth().allowedMethods(new String[] {"POST"}).auth()
.exposedHeaders(new String[] {"Access-Control-Allow-Methods"});
return cors.builder(Response.ok(null, MediaType.APPLICATION_JSON_TYPE)).build();
}

private void checkClient() {
HostnameProvider hostnameProvider = session.getProvider(HostnameProvider.class);

AuthorizeClientUtil.ClientAuthResult clientAuth = AuthorizeClientUtil.authorizeClient(session, event);
ClientModel client = clientAuth.getClient();
}

This returns Status OK.

I don't know how to check client credentials here since I get exception in checkClient() method (the method call in the above code is commented out).

This is the exception I get:
 (default task-1) Error processing grant request: : java.lang.NullPointerException
at org.keycloak.models.KeycloakUriInfo.<init>(KeycloakUriInfo.java:46)
at org.keycloak.services.DefaultKeycloakContext.getUri(DefaultKeycloakContext.java:79)
at org.keycloak.services.DefaultKeycloakContext.getUri(DefaultKeycloakContext.java:86)
at org.keycloak.protocol.oidc.endpoints.TokenEndpoint.checkSsl(TokenEndpoint.java:220)

The exception occurs because hostnameProvider is null in KeycloakUriInfo.

HostnameProvider hostnameProvider = session.getProvider(HostnameProvider.class);

Why is HostnameProvider null? Is there any other way I can check the client credentials validity in the above code?

Thank you and kind regards,
Lidija

On Monday, May 11, 2020 at 5:09:34 PM UTC+2, Phil Fleischer wrote:
I thought grants were a super set of “roles/scopes/mappers” so if you turn on the client “consent” piece then for any specific role, scope, or mapper then they would show up as a line item on the grant page.

So perhaps review the “consent required” sections of the client documentation?

Consent Required

If this is on, then users will get a consent page which asks the user if they grant access to that application. It will also display the metadata that the client is interested in so that the user knows exactly what information the client is getting access to. If you’ve ever done a social login to Google, you’ll often see a similar page. Keycloak provides the same functionality.

On May 11, 2020, at 5:13 AM, lidijaldo <lidi...@gmail.com> wrote:

Hi, 

I didn't find any documentation on implementing a custom grant type in Keycloak. 
How can this be done?

Thank you and kind regards,
Lidija

--
You received this message because you are subscribed to the Google Groups "Keycloak User" group.
To unsubscribe from this group and stop receiving emails from it, send an email to keyclo...@googlegroups.com.

Phil Fleischer

unread,
May 11, 2020, 1:49:40 PM5/11/20
to lidijaldo, Keycloak User
Oh, that grant type!  you can always create a custom rest endpoint.

If you really need to change the token endpoint you’d probably have to override the provider “org.keycloak.protocol.LoginProtocolFactory” and either import/extend the token manager and token endpoints.

— Phil

To unsubscribe from this group and stop receiving emails from it, send an email to keycloak-use...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/keycloak-user/940ee9b7-98fd-4052-9c07-033fdb2075d5%40googlegroups.com.

lidijaldo

unread,
May 13, 2020, 6:47:51 AM5/13/20
to Keycloak User
Thank you, Phil.

I created a custom provider. 
In my custom TokenEndpoint I implemented the methods checkSsl(), checkRealm(), checkRegistrationGrantType() that are almost identical to the private methods in TokenEndpoint. The checkClient() method now correctly verifies client credentials.

@Override
@POST
public Response processGrantRequest() {
MultivaluedMap<String, String> formParams = this.request.getDecodedFormParameters();

    // set grant type
this.grantType = (String) formParams.getFirst(GRANT_TYPE_PARAMETER);

if (grantType.equals(GRANT_TYPE_REGISTER)) {
this.cors = Cors.add(this.request).auth().allowedMethods(new String[] {"POST"}).auth()
.exposedHeaders(new String[] {"Access-Control-Allow-Methods"});

MultivaluedMap<String, Object> outputHeaders = this.httpResponse.getOutputHeaders();
outputHeaders.putSingle("Cache-Control", "no-store");
outputHeaders.putSingle("Pragma", "no-cache");
this.checkSsl();
this.checkRealm();
this.checkRegistrationGrantType();

// check client
this.checkClient();

// custom registration logic
return register();
} else {
// process all Keycloak-supported grant types as normal
return super.processGrantRequest();
}
}

Thank you and kind regards,
Lidija

On Monday, May 11, 2020 at 7:49:40 PM UTC+2, Phil Fleischer wrote:
Oh, that grant type!  you can always create a custom rest endpoint.

If you really need to change the token endpoint you’d probably have to override the provider “org.keycloak.protocol.LoginProtocolFactory” and either import/extend the token manager and token endpoints.

— Phil
Reply all
Reply to author
Forward
0 new messages