Passing Enduser Attributes to the Backend Using JWT
JSON Web Token (JWT) is used to represent claims that are transferred between two parties such as the enduser and the backend.
A claim is an attribute of the user that is mapped to the underlying user store. It is encoded as a JavaScript Object Notation (JSON) object that is used as the payload of a JSON Web Signature (JWS) structure, or as the plain text of a JSON Web Encryption (JWE) structure. This enables claims to be digitally signed.
A set of claims is called a dialect (e.g., http://wso2.org/claims
). The general format of a JWT is {token infor}.{claims list}.{signature}
. The API implementation uses information such as logging, content filtering and authentication/authorization that is stored in this token. The token is Base64-encoded and sent to the API implementation in a HTTP header variable. The JWT is self-contained and is divided into three parts as the header, the payload and the signature. For more information on JWT, see http://openid.net/specs/draft-jones-json-web-token-07.html#anchor3 .
To authenticate endusers, the API Manager passes attributes of the API invoker to the backend API implementation using JWT. In most production deployments, service calls go through the API Manager or a proxy service. If you enable JWT generation in the API Manager, each API request will carry a JWT to the back-end service. When the request goes through the API manager, the JWT is appended as a transport header to the outgoing message. The back-end service fetches the JWT and retrieves the required information about the user, application, or token.
An example of a JWT is given below:
{ "typ":"JWT", "alg":"NONE" }{ "iss":"wso2.org/products/am", "exp":1345183492181, "http://wso2.org/claims/subscriber":"admin", "http://wso2.org/claims/applicationname":"app2", "http://wso2.org/claims/apicontext":"/placeFinder", "http://wso2.org/claims/version":"1.0.0", "http://wso2.org/claims/tier":"Silver", "http://wso2.org/claims/enduser":"sumedha" }
The above token contains,
- Token expiration time ("exp")
- Subscriber to the API, usually the app developer ("http://wso2.org/claims/subscriber")
- Application through which API invocation is done ("http://wso2.org/claims/applicationname")
- Context of the API ("http://wso2.org/claims/apicontext")
- API version ("http://wso2.org/claims/version")
- Tier/price band for the subscription ("http://wso2.org/claims/tier")
- Enduser of the app who's action invoked the API ("http://wso2.org/claims/enduser")
Let's see how to enable and pass information in the JWT or completely alter the JWT generation logic in the API Manager:
Configuring JWT
Before passing enduser attributes, you enable and configure the JWT implementation in the <APIM_HOME>/repository/conf/api-manager.xml
file. The relevant elements are described below. If you do not configure these elements, they take their default values.
Element | Description | Default Value | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
<EnableTokenGeneration> | Set this value to true to enable JWT. | false | ||||||||
<SecurityContextHeader/>
| The name of the HTTP header to which the JWT is attached. | X-JWT-Assertion | ||||||||
<ClaimsRetrieverImplClass/>
| By default, the <ClaimsRetrieverImplClass>org.wso2.carbon.apimgt.impl.token.DefaultClaimsRetriever</ClaimsRetrieverImplClass> By default, the following are encoded to the JWT:
In addition, you can also write your own class by extending the interface
| org.wso2.carbon.apimgt.impl.token.DefaultClaimsRetriever | ||||||||
<ConsumerDialectURI/>
| The dialect URI under which the user's claims are be looked for. Only works with the default value of The JWT token contains all claims define in the | http://wso2.org/claims | ||||||||
<SignatureAlgorithm/> | The signing algorithm used to sign the JWT. The general format of the JWT is This element can have only two values- the default value, which is SHA256WITHRSA or NONE. | SHA256WITHRSA |
You can use TCPMon or API Gateway debug logs to capture JWT token header withenduser details. To enable gateway DEBUG logs for wire messages,
- Go to the
<APIM_GATEWAY>/repository/conf
directory and open thelog4j.properties
file with a text editor. - Edit the entries for the two loggers as follows:
#log4j.logger.org.apache.synapse.transport.http.headers=DEBUG
#log4j.logger.org.apache.synapse.transport.http.wire=DEBUG
Customize the JWT generation
The JWT that is generated by default (see example above) has predefined attributes that are passed to the backend. These include basic application-specific details, subscription details, and user information that are defined in the JWT generation class that comes with the API Manager by the name org.wso2.carbon.apimgt.impl.token.JWTGenerator
. If you want to pass additional attributes to the backend with the JWT or completely change the default JWT generation logic, do the following:
Write your own custom JWT implementation class by extending the default
JWTGenerator
class. A typical example of implementing your own claim generator is given below. It implements thepopulateCustomClaims()
method to generate some custom claims and adds them to the JWT.import org.wso2.carbon.apimgt.impl.APIConstants; import org.wso2.carbon.apimgt.impl.dto.APIKeyValidationInfoDTO; import org.wso2.carbon.apimgt.impl.token.JWTGenerator; import org.wso2.carbon.apimgt.api.*; import java.util.Map; public class CustomTokenGenerator extends JWTGenerator { public Map populateStandardClaims(APIKeyValidationInfoDTO keyValidationInfoDTO, String apiContext, String version) throws APIManagementException { Map claims = super.populateStandardClaims(keyValidationInfoDTO, apiContext, version); boolean isApplicationToken = keyValidationInfoDTO.getUserType().equalsIgnoreCase(APIConstants.ACCESS_TOKEN_USER_TYPE_APPLICATION) ? true : false; String dialect = getDialectURI(); if (claims.get(dialect + "/enduser") != null) { if (isApplicationToken) { claims.put(dialect + "/enduser", "null"); claims.put(dialect + "/enduserTenantId", "null"); } else { String enduser = claims.get(dialect + "/enduser"); if (enduser.endsWith("@carbon.super")) { enduser = enduser.replace("@carbon.super", ""); claims.put(dialect + "/enduser", enduser); } } } return claims; } public Map populateCustomClaims(APIKeyValidationInfoDTO keyValidationInfoDTO, String apiContext, String version, String accessToken) throws APIManagementException { Long time = System.currentTimeMillis(); String text = "This is custom JWT"; Map map = new HashMap(); map.put("current_timestamp", time.toString()); map.put("messge" , text); return map; } }
- Build your class and add the JAR file to
<APIM_HOME>/repository/components/lib
directory. Add your class in the <TokenGeneratorImpl> element of the
<APIM_HOME>/repository/conf/api-manager.xml
file.<APIConsumerAuthentication> .... <TokenGeneratorImpl>org.wso2.carbon.test.CustomTokenGenerator</TokenGeneratorImpl> .... </APIConsumerAuthentication>
- Set the
<EnableTokenGeneration>
element to true in theapi-manager.xml
file. - Restart the server.