All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.authlete.jaxrs.spi.TokenRequestHandlerSpi Maven / Gradle / Ivy

There is a newer version: 2.9
Show newest version
/*
 * Copyright (C) 2015-2025 Authlete, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific
 * language governing permissions and limitations under the
 * License.
 */
package com.authlete.jaxrs.spi;


import java.util.Map;
import javax.ws.rs.core.Response;
import com.authlete.common.dto.Property;
import com.authlete.common.dto.TokenResponse;
import com.authlete.jaxrs.TokenRequestHandler;


/**
 * Service Provider Interface to work with {@link
 * com.authlete.jaxrs.TokenRequestHandler TokenRequestHandler}.
 *
 * 

* An implementation of this interface must be given to the constructor * of {@link com.authlete.jaxrs.TokenRequestHandler TokenRequestHandler} * class. *

* * @author Takahiko Kawasaki */ public interface TokenRequestHandlerSpi { /** * Authenticate an end-user. * *

* This method is called only when Resource Owner * Password Credentials Grant was used. Therefore, if you have * no mind to support Resource Owner Password Credentials, always * return {@code null}. In typical cases, you don't have to support * Resource Owner Password Credentials Grant. * FYI: RFC 6749 says "The authorization server should take special * care when enabling this grant type and only allow it when other * flows are not viable." *

* *

* Below is an example implementation using Apache Shiro. *

* *
*
     * @Override
     * public String authenticateUser(String username, String password)
     * {
     *     // Pack the username and password into AuthenticationToken
     *     // which Apache Shiro's SecurityManager can accept.
     *     AuthenticationToken credentials =
     *         new UsernamePasswordToken(username, password);
     *
     *     try
     *     {
     *         // Authenticate the resource owner.
     *         AuthenticationInfo info =
     *             SecurityUtils.getSecurityManager().authenticate(credentials);
     *
     *         // Get the subject of the authenticated user.
     *         return info.getPrincipals().getPrimaryPrincipal().toString();
     *     }
     *     catch (AuthenticationException e)
     *     {
     *         // Not authenticated.
     *         return null;
     *     }
     * }
*
* * @param username * The value of {@code username} parameter in the token request. * * @param password * The value of {@code password} parameter in the token request. * * @return * The subject (= unique identifier) of the authenticated * end-user. If the pair of {@code username} and {@code * password} is invalid, {@code null} should be returned. */ String authenticateUser(String username, String password); /** * Get extra properties to associate with an access token. * *

* This method is expected to return an array of extra properties. * The following is an example that returns an array containing one * extra property. *

* *
     * @Override
     * public {@link Property}[] getProperties()
     * {
     *     return new {@link Property}[] {
     *         new {@link Property#Property(String, String)
     *     Property}("example_parameter", "example_value")
     *     };
     * }
* *

* Extra properties returned from this method will appear as top-level entries * in a JSON response from an authorization server as shown in 5.1. Successful Response * in RFC 6749. *

* *

* Keys listed below should not be used and they would be ignored on * the server side even if they were used. It's because they are reserved * in RFC 6749 and * OpenID Connect Core 1.0. *

* *
    *
  • {@code access_token} *
  • {@code token_type} *
  • {@code expires_in} *
  • {@code refresh_token} *
  • {@code scope} *
  • {@code error} *
  • {@code error_description} *
  • {@code error_uri} *
  • {@code id_token} *
* *

* Note that there is an upper limit on the total size of extra properties. * On the server side, the properties will be (1) converted to a multidimensional * string array, (2) converted to JSON, (3) encrypted by AES/CBC/PKCS5Padding, (4) * encoded by base64url, and then stored into the database. The length of the * resultant string must not exceed 65,535 in bytes. This is the upper limit, but * we think it is big enough. *

* *

* When the value of {@code grant_type} parameter contained in the token request * from the client application is {@code authorization_code} or {@code refresh_token}, * extra properties are merged. Rules are as described in the table below. *

* *
* * * * * * * * * * * * * * * * *
grant_typeDescription
authorization_code *

* If the authorization code presented by the client application already * has extra properties (this happens if {@link * AuthorizationDecisionHandlerSpi#getProperties()} returned extra properties * when the authorization code was issued), extra properties returned by this * method will be merged into the existing extra properties. Note that the * existing extra properties will be overwritten if extra properties returned * by this method have the same keys. *

*

* For example, if an authorization code has two extra properties, {@code a=1} * and {@code b=2}, and if this method returns two extra properties, {@code a=A} * and {@code c=3}, the resultant access token will have three extra properties, * {@code a=A}, {@code b=2} and {@code c=3}. *

*
refresh_token *

* If the access token associated with the refresh token presented by the * client application already has extra properties, extra properties returned * by this method will be merged into the existing extra properties. Note that * the existing extra properties will be overwritten if extra properties * returned by this method have the same keys. *

*
*
* * @return * Extra properties. If {@code null} is returned, any extra * property will not be associated. * * @since 1.3 */ Property[] getProperties(); /** * Handle a token exchange request. * *

* This method is called when the grant type of the token request is * {@code "urn:ietf:params:oauth:grant-type:token-exchange"} (but see the * "NOTE 2" below). The grant type is defined in RFC 8693: OAuth 2.0 Token * Exchange. *

* *

* RFC 8693 is very flexible. In other words, the specification does not * define details that are necessary for secure token exchange. Therefore, * implementations have to complement the specification with their own * rules. *

* *

* The argument passed to this method is an instance of {@link TokenResponse} * that represents a response from Authlete's {@code /auth/token} API. The * instance contains information about the token exchange request such as * the value of the {@code subject_token} request parameter. Implementations * of this {@code tokenExchange} method are supposed to (1) validate the * information based on their own rules, (2) generate a token (e.g. an access * token) using the information, and (3) prepare a token response in the JSON * format that conforms to Section 2.2 * of RFC 8693. *

* *

* Authlete's {@code /auth/token} API performs validation of token exchange * requests to some extent. Therefore, authorization server implementations * don't have to repeat the same validation steps. See the JavaDoc of the * {@link TokenResponse} class for details about the validation steps. *

* *

* NOTE 1: Token Exchange is supported by Authlete 2.3 and newer versions. * If the Authlete server of your system is older than version 2.3, the grant * type ({@code "urn:ietf:params:oauth:grant-type:token-exchange"}) is not * supported and so this method is never called. *

* *

* NOTE 2: Even if the grant type is * {@code "urn:ietf:params:oauth:grant-type:token-exchange"}, if the service * is configured to support the "OpenID * Connect Native SSO for Mobile Apps 1.0" specification (a.k.a. * "Native SSO") and the token request complies with the specification, * the {@link #nativeSso(TokenResponse, Map) nativeSso} method is called * instead of this {@code tokenExchange} method. Native SSO is supported * in Authlete 3.0 and newer versions. *

* * @param tokenResponse * A response from Authlete's {@code /auth/token} API. * * @param headers * HTTP headers that should be included in the token response. For * example, this map may include a key-value pair consisting of the * {@code DPoP-Nonce} header and a DPoP nonce value (see RFC 9449: * OAuth 2.0 Demonstrating Proof of Possession (DPoP), Section 8. * Authorization Server-Provided Nonce). This {@code headers} * parameter was introduced in version 2.86 of authlete-java-jaxrs. * This is a breaking change. * * @return * A response from the token endpoint. It must conform to Section * 2.2 of RFC 8693. If this method returns {@code null}, * {@link TokenRequestHandler} will generate {@code 400 Bad Request} * with {"error":"unsupported_grant_type"}. * * @since 2.47 * @since Authlete 2.3 * * @see RFC 8693 OAuth 2.0 Token Exchange */ Response tokenExchange(TokenResponse tokenResponse, Map headers); /** * Handle a token request that uses the grant type * {@code "urn:ietf:params:oauth:grant-type:jwt-bearer"} (RFC 7523). * *

* This method is called when the grant type of the token request is * {@code "urn:ietf:params:oauth:grant-type:jwt-bearer"}. The grant type * is defined in RFC 7523: JSON Web Token (JWT) Profile for OAuth 2.0 Client * Authentication and Authorization Grants. *

* *

* The grant type utilizes a JWT as an authorization grant, but the * specification does not define details about how the JWT is generated * by whom. As a result, it is not defined in the specification how to * obtain the key whereby to verify the signature of the JWT. Therefore, * each deployment has to define their own rules which are necessary to * determine the key for signature verification. *

* *

* The argument passed to this method is an instance of {@link TokenResponse} * that represents a response from Authlete's {@code /auth/token} API. The * instance contains information about the token request such as the value * of the {@code assertion} request parameter. Implementations of this * {@code jwtBearer} method are supposed to (1) validate the authorization * grant (= the JWT specified by the {@code assertion} request parameter), * (2) generate an access token, and (3) prepare a token response in the * JSON format that conforms to RFC 6749. *

* *

* Authlete's {@code /auth/token} API performs validation of token requests * to some extent. Therefore, authorization server implementations don't * have to repeat the same validation steps. Basically, what implementations * have to do is to verify the signature of the JWT. See the JavaDoc of the * {@link TokenResponse} class for details about the validation steps. *

* *

* NOTE: JWT Authorization Grant is supported by Authlete 2.3 and newer * versions. If the Authlete server of your system is older than version * 2.3, the grant type ({@code "urn:ietf:params:oauth:grant-type:jwt-bearer"}) * is not supported and so this method is never called. *

* * @param tokenResponse * A response from Authlete's {@code /auth/token} API. * * @param headers * HTTP headers that should be included in the token response. For * example, this map may include a key-value pair consisting of the * {@code DPoP-Nonce} header and a DPoP nonce value (see RFC 9449: * OAuth 2.0 Demonstrating Proof of Possession (DPoP), Section 8. * Authorization Server-Provided Nonce). This {@code headers} * parameter was introduced in version 2.86 of authlete-java-jaxrs. * This is a breaking change. * * @return * A response from the token endpoint. It must conform to RFC 6749. If * this method returns {@code null}, {@link TokenRequestHandler} * will generate {@code 400 Bad Request} with * {"error":"unsupported_grant_type"}. * * @since 2.48 * @since Authlete 2.3 * * @see RFC 7521 * Assertion Framework for OAuth 2.0 Client Authentication and * Authorization Grants * * @see RFC 7523 * JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication * and Authorization Grants */ Response jwtBearer(TokenResponse tokenResponse, Map headers); /** * Handle a token request that complies with the "OpenID * Connect Native SSO for Mobile Apps 1.0" specification * (also known as "Native SSO"). * *

* This method is called if the {@code action} parameter in the response * from Authlete's {@code /auth/token} API is {@link * com.authlete.common.dto.TokenResponse.Action#NATIVE_SSO NATIVE_SSO}. * This action value is returned when one of the following condition sets * is satisfied. *

* *
    *
  1. Authorization Code Flow *
      *
    • The service's {@code nativeSsoSupported} property is set to * {@code true}. *
    • The service supports the {@code openid} and {@code device_sso} scopes. *
    • The client is allowed to request the {@code openid} and * {@code device_sso} scopes. *
    • The grant type of the token request is {@code authorization_code}. *
    • The authorization request preceding the token request included the * {@code openid} and {@code device_sso} scopes. *
    *
  2. Refresh Token Flow *
      *
    • The service's {@code nativeSsoSupported} property is set to * {@code true}. *
    • The service supports the {@code device_sso} scope. *
    • The client is allowed to request the {@code device_sso} scope. *
    • The grant type of the token request is {@code refresh_token}. *
    • The access token issued by the refresh token request still covers the * {@code device_sso} scope, even if the scope coverage might have been * narrowed. *
    • The presented refresh token is associated with a user's authentication * session. (In practice, only refresh tokens generated through the * authorization code flow compliant with Native SSO can be used.) *
    *
  3. Token Exchange Flow *
      *
    • The service's {@code nativeSsoSupported} property is set to * {@code true}. *
    • The grant type of the token request is * {@code urn:ietf:params:oauth:grant-type:token-exchange}. *
    • The value of the {@code actor_token_type} request parameter is * {@code urn:openid:params:token-type:device-secret}. *
    *
* *

Session ID

*

* When the {@code action} value is {@code NATIVE_SSO}, the response from the * {@code /auth/token} API contains a {@code sessionId} parameter. Its value * represents a user's authentication session - that is, a session ID. *

* *

* The authorization server must check whether the session ID is still valid. * Note that the session ID is not a value generated by Authlete but one that * was passed from the authorization server to the * {@code /auth/authorization/issue} API. Therefore, Authlete does not and * cannot determine whether the session ID is still valid. *

* *

* If the session ID is no longer valid, the authorization server should return * an error response from the token endpoint with the error code * {@code invalid_grant}. *

* *

Device Secret

* *
Case 1: Device Secret in Authorization Code and Refresh Token Flows
*

* When the grant type is {@code authorization_code} or {@code refresh_token}, * the response from the {@code /auth/token} API may contain a * {@code deviceSecret} parameter. Its value represents a device secret passed * from the client application as the value of the {@code device_secret} request * parameter to the token endpoint. This request parameter is optional. *

* *

* When the {@code deviceSecret} parameter in the response from the * {@code /auth/token} API is not null, the authorization server must check * whether the device secret is valid. If the device secret is valid, the value * should be passed to the {@code /nativesso} API later without modification, * unless the authorization server chooses to reissue a new device secret. *

* *

* On the other hand, if the {@code deviceSecret} parameter is absent or its * value is invalid, the authorization server must generate a new device * secret. The new value should then be passed to the {@code /nativesso} API. *

* *

* Note that Authlete neither generates nor manages device secrets. It is the * authorization server's responsibility to do so. Therefore, Authlete does * not and cannot determine whether a device secret is valid. *

* *
Case 2: Device Secret in Token Exchange Flow
*

* When the grant type is * {@code urn:ietf:params:oauth:grant-type:token-exchange}, the response from * the {@code /auth/token} API contains {@code deviceSecret} and * {@code deviceSecretHash} parameters. *

* *

* The {@code deviceSecret} parameter represents the device secret presented * by the client application to the token endpoint as the value of the * {@code actor_token} request parameter. *

* *

* The {@code deviceSecretHash} parameter represents the device secret hash * embedded as the value of the {@code ds_hash} claim in the ID token that * the client application passed to the token endpoint as the value of the * {@code subject_token} request parameter. *

* *

* The authorization server must verify the binding between the device secret * and device secret hash. If the binding fails verification, the authorization * server should return an error response from the token endpoint with the * error code {@code invalid_grant}. *

* *

* Note that the Native SSO specification does not define how to compute a * device secret hash value from a device secret. The specification states, * "The exact binding between the ds_hash and * device_secret is not specified by this profile." Therefore, * the authorization server must define a rule regarding for computing the * device hash value and verify the binding based on that rule. A simple * example of hash computation logic is to compute the SHA-256 hash of a * device secret and base64url-encode the hash. *

* *
/nativesso API Call
*

* After validating the session ID, device secret, and device secret hash as * necessary, the authorization server must call the {@code /nativesso} API * to generate a Native SSO-compliant ID token and token response. The API * expects the following request parameters. *

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
ParameterDescription
accessToken *

* REQUIRED. If the response from the {@code /auth/token} API contains * the {@code jwtAccessToken} parameter, its value must be used as the * value of this {@code accessToken} request parameter to the * {@code /nativesso} API. If the {@code jwtAccessToken} parameter is * absent, the value of the {@code accessToken} parameter in the response * from the {@code /auth/token} API should be used instead. *

*

* The specified value is used as the value of the {@code access_token} * property in the token response. *

*
refreshToken *

* OPTIONAL. If the {@code refreshToken} parameter is present in the * response from the {@code /auth/token} API, its value should be * specified as the value of this {@code refreshToken} request parameter * to the {@code /nativesso} API. Note that whether a refresh token is * issued depends on configuration. *

*

* The specified value is used as the value of the {@code refresh_token} * property in the token response. *

*
deviceSecret *

* REQUIRED. If the response from the {@code /auth/token} API contains * the {@code deviceSecret} parameter, its value should be used as the * value of this {@code deviceSecret} request parameter to the * {@code /nativesso} API. The authorization server may choose to issue * a new device secret; in that case, it is free to generate a new device * secret and specify the new value. *

*

* If the response from the {@code /auth/token} API does not contain the * {@code deviceSecret} parameter, or if its value is invalid, the * authorization server must generate a new device secret and specify it * in the {@code deviceSecret} parameter to the {@code /nativesso} API. *

*

* The specified value is used as the value of the {@code device_secret} * property in the token response. *

*
deviceSecretHash *

* RECOMMENDED. The authorization server should compute the hash value * of the device secret based on its own logic and specify the computed * hash as the value of this {@code deviceSecretHash} request parameter * to the {@code /nativesso} API. *

*

* When the {@code deviceSecretHash} parameter is omitted, the * implementation of the {@code /nativesso} API generates the device * secret hash by computing the SHA-256 hash of the device secret and * encoding it with base64url. Note that this hash computation logic is * not a rule defined in the Native SSO specification; rather, it is * Authlete-specific fallback logic used when the {@code deviceSecretHash} * parameter is omitted. *

*
sub *

* OPTIONAL. The value of the {@code sub} claim to be embedded in the ID * token. If omitted, the subject associated with the access token is * used as the value of the {@code sub} claim. *

*
claims *

* OPTIONAL. Additional claims to be embedded in the ID token. The format * of this parameter must be a JSON object. *

*
idtHeaderParams *

* OPTIONAL. Additional parameters to be embedded in the JWS header of * the ID token. The format of this parameter must be a JSON object. *

*
idTokenAudType *

* OPTIONAL. This parameter specifies the type of the {@code aud} claim * in the ID token. If {@code "array"} is specified, the {@code aud} * claim will be a JSON array. If {@code "string"} is specified, it will * be a JSON string. If omitted, the {@code aud} claim will default to a * JSON array. *

*
* *

* On success, the {@code action} parameter in the response from the * {@code /nativesso} API is {@code OK}. In this case, the value of the * {@code responseContent} parameter in the response can be used as the message * body of the token response from the token endpoint. The token endpoint * implementation can construct the token response as follows: *

* *
     * HTTP/1.1 200 OK
     * Content-Type: application/json
     * Cache-Control: no-cache, no-store
     *
     * (Embed the value of the responseContent parameter in the response
     *  from the /nativesso API here)
     * 
* *

* The resulting message body will look like this: *

* *
     * {
     *   "access_token":      "(Access Token)",
     *   "token_type":        "(Token Type)",
     *   "expires_in":         (Lifetime in Seconds),
     *   "scope":             "(Space-separated Scopes)",
     *   "refresh_token":     "(Refresh Token)",
     *   "id_token":          "(ID Token)",
     *   "device_secret":     "(Device Secret)",
     *   "issued_token_type": "urn:ietf:params:oauth:token-type:access_token"
     * }
* *

* The {@code issued_token_type} parameter in the token response is required * only in the token exchange flow, but the current implementation of the * {@code /nativesso} API always embeds the parameter, even in the authorization * code and refresh token flows. Since it is allowed to include any parameters * in token responses, this behavior should not cause any issues. *

* * @param tokenResponse * A response from Authlete's {@code /auth/token} API. * * @param headers * HTTP headers that should be included in the token response. For * example, this map may include a key-value pair consisting of the * {@code DPoP-Nonce} header and a DPoP nonce value (see RFC 9449: * OAuth 2.0 Demonstrating Proof of Possession (DPoP), Section 8. * Authorization Server-Provided Nonce). * * @return * A response from the token endpoint. It must conform to the * "OpenID Connect Native SSO for Mobile Apps 1.0" specification. * * @since 2.86 * @since Authlete 3.0 * * @see OpenID Connect Native SSO for Mobile Apps 1.0 */ Response nativeSso(TokenResponse tokenResponse, Map headers); }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy