Understanding JWT for apps (2024)

There is a nicely presented copy of the specification.JSON Web Token (JWT) is a compact URL-safe means of representing claims to be transferred between two parties.The claims in a JWT are encoded as a JavaScript Object Notation (JSON) object that is used as the payload of aJSON Web Signature (JWS) structure or as the plaintext of a JSON Web Encryption (JWE) structure, enabling the claims tobe digitally signed or MACed and/or encrypted.

Structure of a JWT token

A JWT token looks like this:

12
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjEzODY4OTkxMzEsImlzcyI6ImppcmE6MTU0ODk1OTUiLCJxc2giOiI4MDYzZmY0Y2ExZTQxZGY3YmM5MGM4YWI2ZDBmNjIwN2Q0OTFjZjZkYWQ3YzY2ZWE3OTdiNDYxNGI3MTkyMmU5IiwiaWF0IjoxMzg2ODk4OTUxfQ.uKqU9dTB6gKwG6jQCuXYAiMNdfNRw98Hw_IWuA5MaMo

Once you understand the format, it's actually pretty simple:

12
<base64-encoded header>.<base64-encoded claims>.<base64-encoded signature>

In other words:

  • You create a header object, with the JSON format, then you encode it as a base64.
  • You create a claims object, with the JSON format, then you encode it in base64.
  • You create a signature for the URI (we'll get into that later), then you encode it in base64.
  • You concatenate the three items, with the "." separator.

You shouldn't actually have to do this manually, as there are libraries available in most languages, as we describe in the JWT libraries section.However it is important you understand the fields in the JSON header and claims objects described in the next sections.

JWT header

The JWT header declares that the encoded object is a JSON Web Token (JWT) and the JWT is a JWS that is MACed using the HMAC SHA-256 algorithm. For example:

AttributeTypeDescription
typ (mandatory)StringType for the token, defaulted to JWT; specifies that this is a JWT token.
alg (mandatory)StringAlgorithm; specifies the algorithm used to sign the token. In atlassian-connect version 1.0 we support the HMAC SHA-256 algorithm, which the JWT specification identifies using the string "HS256".

Your JWT library or implementation should discard any tokens which specify `alg: none` as this can provide a bypass of the token verification.

Claims

The JWT claims object contains security information about the message. For example:

12
{ 'iss': 'com.example.app', 'iat': 1433780963, 'qsh': '88396352255a6c933def07620a3281c9e27d8c668e0f4d01a8ecdbb74ca52c97', 'sub': 'connection:479', 'exp': 1437380963}
AttributeTypeDescription
iss (mandatory)StringThe issuer of the claim. Connect uses it to identify the application making the call. For example:
  • If the Atlassian product is the calling application: contains the unique identifier of the tenant. This is the clientKey that you receive in the installed callback. You should reject unrecognized issuers.
  • If the app is the calling application: the app key specified in the app descriptor.
iat (mandatory)LongIssued-at time. Contains the UTC Unix time at which this token was issued. There are no hard requirements around this claim but it does not make sense for it to be significantly in the future. Also, significantly old issued-at times may indicate the replay of suspiciously old tokens.
exp (mandatory)LongExpiration time. It contains the UTC Unix time after which you should no longer accept this token. It should be after the issued-at time.
qsh (mandatory)StringQuery string hash. A custom Atlassian claim that prevents URL tampering.
sub (mandatory)StringThe subject of this token. The subject of this token. This is the user associated with the relevant action. In Bitbucket's case the sub claim is associated with the connection key, as shown in the following example.{ 'iss': 'com.example.app', 'iat': 1433780963, 'qsh': '88396352255a6c933def07620a3281c9e27d8c668e0f4d01a8ecdbb74ca52c97', 'sub': 'connection:479', 'exp': 1437380963}
aud (Unavailable)String or StringCurrently, in Connect for Bitbucket Cloud, you cannot use the aud claim. Doing so will cause your app to fail.

You should use a little leeway when processing time-based claims, as clocks may drift apart.The JWT specification suggests no more than a few minutes.Judicious use of the time-based claims allows for replays within a limited window.This can be useful when all or part of a page is refreshed or when it is valid for a user torepeatedly perform identical actions (e.g., clicking the same button).

JWT libraries

Most modern languages have JWT libraries available. We recommend you use one of these libraries(or other JWT-compatible libraries) before trying to hand-craft the JWT token.

The JWT decoder is a handy web-based decoder for Atlassian Connect JWT tokens.

Creating a JWT token

Here is an example of creating a JWT token, in Java, using atlassian-jwt and nimbus-jwt:

12
import java.io.UnsupportedEncodingException;import java.security.NoSuchAlgorithmException;import java.util.HashMap;import com.atlassian.jwt.*;import com.atlassian.jwt.core.writer.*;import com.atlassian.jwt.httpclient.CanonicalHttpUriRequest;import com.atlassian.jwt.writer.JwtJsonBuilder;import com.atlassian.jwt.writer.JwtWriterFactory;public class JWTSample { public String createUriWithJwt() throws UnsupportedEncodingException, NoSuchAlgorithmException { long issuedAt = System.currentTimeMillis() / 1000L; long expiresAt = issuedAt + 180L; String key = "atlassian-connect-app"; //the key from the app descriptor String sharedSecret = "..."; //the sharedsecret key received //during the app installation handshake String method = "GET"; String baseUrl = "http://localhost:2990/jira"; String contextPath = "/jira"; String apiPath = "/rest/api/latest/serverInfo"; JwtJsonBuilder jwtBuilder = new JsonSmartJwtJsonBuilder() .issuedAt(issuedAt) .expirationTime(expiresAt) .issuer(key); CanonicalHttpUriRequest canonical = new CanonicalHttpUriRequest(method, apiPath, contextPath, new HashMap()); JwtClaimsBuilder.appendHttpRequestClaims(jwtBuilder, canonical); JwtWriterFactory jwtWriterFactory = new NimbusJwtWriterFactory(); String jwtbuilt = jwtBuilder.build(); String jwtToken = jwtWriterFactory.macSigningWriter(SigningAlgorithm.HS256, sharedSecret).jsonToJwt(jwtbuilt); String apiUrl = baseUrl + apiPath + "?jwt=" + jwtToken; return apiUrl; }}

Decoding and verifying a JWT token

Here is a minimal example of decoding and verifying a JWT token, in Java, using atlassian-jwt and nimbus-jwt.Note: This example does not include any error handling.See AbstractJwtAuthenticatorfrom atlassian-jwt for recommendations of how to handle the different error cases.

12
import com.atlassian.jwt.*;import com.atlassian.jwt.core.http.JavaxJwtRequestExtractor;import com.atlassian.jwt.core.reader.*;import com.atlassian.jwt.exception.*;import com.atlassian.jwt.reader.*;import javax.servlet.http.HttpServletRequest;import java.io.UnsupportedEncodingException;import java.security.NoSuchAlgorithmException;import java.util.Map;public class JWTVerificationSample { public Jwt verifyRequest(HttpServletRequest request, JwtIssuerValidator issuerValidator, JwtIssuerSharedSecretService issuerSharedSecretService) throws UnsupportedEncodingException, NoSuchAlgorithmException, JwtVerificationException, JwtIssuerLacksSharedSecretException, JwtUnknownIssuerException, JwtParseException { JwtReaderFactory jwtReaderFactory = new NimbusJwtReaderFactory( issuerValidator, issuerSharedSecretService); JavaxJwtRequestExtractor jwtRequestExtractor = new JavaxJwtRequestExtractor(); CanonicalHttpRequest canonicalHttpRequest = jwtRequestExtractor.getCanonicalHttpRequest(request); Map<String, ? extends JwtClaimVerifier> requiredClaims = JwtClaimVerifiersBuilder.build(canonicalHttpRequest); String jwt = jwtRequestExtractor.extractJwt(request); return jwtReaderFactory.getReader(jwt).readAndVerify(jwt, requiredClaims); }}

Decoding a JWT token

Decoding the JWT token reverses the steps followed during the creation of the token,to extract the header, claims and signature. Here is an example in Java:

12
String jwtToken = ...;//e.g. extracted from the requestString[] base64EncodedSegments = jwtToken.split('.');String base64EncodedHeader = base64EncodedSegments[0];String base64EncodedClaims = base64EncodedSegments[1];String signature = base64EncodedSegments[2];String header = base64decode(base64EncodedHeader);String claims = base64decode(base64EncodedClaims);

This gives us the following:

Header:

12
{ "alg": "HS256", "typ": "JWT" }

Claims:

12
{ "iss": "jira:15489595", "iat": 1386898951, "qsh": "8063ff4ca1e41df7bc90c8ab6d0f6207d491cf6dad7c66ea797b4614b71922e9", "exp": 1386899131}

Signature:

12
uKqU9dTB6gKwG6jQCuXYAiMNdfNRw98Hw_IWuA5MaMo

Verifying a JWT token

JWT libraries typically provide methods to be able to verify a received JWT token.Here is an example using nimbus-jose-jwt and json-smart:

12
import com.nimbusds.jose.JOSEException;import com.nimbusds.jose.JWSObject;import com.nimbusds.jose.JWSVerifier;import com.nimbusds.jwt.JWTClaimsSet;import net.minidev.json.JSONObject;public JWTClaimsSet read(String jwt, JWSVerifier verifier) throws ParseException, JOSEException{ JWSObject jwsObject = JWSObject.parse(jwt); if (!jwsObject.verify(verifier)) { throw new IllegalArgumentException("Fraudulent JWT token: " + jwt); } JSONObject jsonPayload = jwsObject.getPayload().toJSONObject(); return JWTClaimsSet.parse(jsonPayload);}

Advanced: Creating a JWT token manually

You should only need to read this section if you are planning to create JWT tokens manually, i.e., if you are not using one of the libraries listed in the previous section.

More details on JWT tokens

The format of a JWT token is simple: <base64-encoded header>.<base64-encoded claims>.<signature>.

  • Each section is separated from the others by a period character (.).
  • Each section is base-64 encoded, so you will need to decode each one to make them human-readable.
  • The header specifies a very small amount of information that the receiver needs in order to parse and verify the JWT token.
  • All JWT token headers state that the type is "JWT".
  • The algorithm used to sign the JWT token is needed so that the receiver can verify the signature.
  • The claims are a list of assertions that the issuer is making: each says that "this named field" has "this value".
  • Some, like the "iss" claim, which identifies the issuer of this JWT token, have standard names and uses.
  • Others are custom claims. We limit our use of custom claims as much as possible, for ease of implementation.
  • The signature is computed by using an algorithm such as HMAC SHA-256 plus the header and claims sections.
  • The receiver verifies that the signature must have been computed using the genuine JWT header and claims sections,the indicated algorithm and a previously established secret.
  • An attacker tampering with the header or claims will cause signature verification to fail.
  • An attacker signing with a different secret will cause signature verification to fail.
  • There are various algorithm choices legal in the JWT spec. In atlassian-connect version 1.0 we support HMAC SHA-256. Important: your implementation should discard any JWT tokens which specify alg: none as these are not subject to signature verification.

Steps to Follow

  1. Create a header JSON object.
  • Convert the header JSON object to a UTF-8 encoded string and base-64 encode it. That gives you encodedHeader.
  • Create a claims JSON object, including a query string hash
  • Convert the claims JSON object to a UTF-8 encoded string and base-64 encode it. That gives you encodedClaims.
  • Concatenate the encoded header, a period character (.) and the encoded claims set. That gives you signingInput = encodedHeader + "." + encodedClaims.
  • Compute the signature of signingInput using the JWT or cryptographic library of your choice. Then base64 encode it. That gives you encodedSignature.
  • Concatenate the signing input, another period character and the signature, which gives you the JWT token. jwtToken = signingInput + "." + encodedSignature.

Example

Here is an example in Java using gson, commons-codec, and the Java security and crypto libraries:

12
public class JwtClaims { protected String iss; protected long iat; protected long exp; protected String qsh; protected String sub; // + getters/setters/constructors}[...]public class JwtHeader { protected String alg; protected String typ; // + getters/setters/constructors}[...]import static org.apache.commons.codec.binary.Base64.encodeBase64URLSafeString;import static org.apache.commons.codec.binary.Hex.encodeHexString;import java.io.UnsupportedEncodingException;import java.security.*;import javax.crypto.*;import javax.crypto.spec.SecretKeySpec;import com.google.gson.Gson;public class JwtBuilder { public static String generateJWTToken(String requestUrl, String canonicalUrl, String key, String sharedSecret) throws NoSuchAlgorithmException, UnsupportedEncodingException, InvalidKeyException { JwtClaims claims = new JwtClaims(); claims.setIss(key); claims.setIat(System.currentTimeMillis() / 1000L); claims.setExp(claims.getIat() + 180L); claims.setQsh(getQueryStringHash(canonicalUrl)); String jwtToken = sign(claims, sharedSecret); return jwtToken; } private static String sign(JwtClaims claims, String sharedSecret) throws InvalidKeyException, NoSuchAlgorithmException { String signingInput = getSigningInput(claims, sharedSecret); String signed256 = signHmac256(signingInput, sharedSecret); return signingInput + "." + signed256; } private static String getSigningInput(JwtClaims claims, String sharedSecret) throws InvalidKeyException, NoSuchAlgorithmException { JwtHeader header = new JwtHeader(); header.alg = "HS256"; header.typ = "JWT"; Gson gson = new Gson(); String headerJsonString = gson.toJson(header); String claimsJsonString = gson.toJson(claims); String signingInput = encodeBase64URLSafeString(headerJsonString .getBytes()) + "." + encodeBase64URLSafeString(claimsJsonString.getBytes()); return signingInput; } private static String signHmac256(String signingInput, String sharedSecret) throws NoSuchAlgorithmException, InvalidKeyException { SecretKey key = new SecretKeySpec(sharedSecret.getBytes(), "HmacSHA256"); Mac mac = Mac.getInstance("HmacSHA256"); mac.init(key); return encodeBase64URLSafeString(mac.doFinal(signingInput.getBytes())); } private static String getQueryStringHash(String canonicalUrl) throws NoSuchAlgorithmException,UnsupportedEncodingException { MessageDigest md = MessageDigest.getInstance("SHA-256"); md.update(canonicalUrl.getBytes("UTF-8")); byte[] digest = md.digest(); return encodeHexString(digest); } }[...]public class Sample { public String getUrlSample() throws Exception { String requestUrl = "http://localhost:2990/jira/rest/atlassian-connect/latest/license"; String canonicalUrl = "GET&/rest/atlassian-connect/latest/license&"; String key = "..."; //from the app descriptor //and received during installation handshake String sharedSecret = "..."; //received during installation Handshake String jwtToken = JwtBuilder.generateJWTToken( requestUrl, canonicalUrl, key, sharedSecret); String restAPIUrl = requestUrl + "?jwt=" + jwtToken; return restAPIUrl; }}
Understanding JWT for apps (2024)

FAQs

Understanding JWT for apps? ›

A JWT comprises three main parts: the header, the payload, and the signature, each encoded in Base64. The header typically includes the token type ( JWT ) and the signing algorithm used ( HS256 , for example). The payload contains claims, such as the issuer, subject, expiration time, and issuance time.

Can we use JWT for mobile app? ›

Get the User Profile

To retrieve the User Profile, your mobile application can decode the ID Token using one of the JWT libraries. This is done by verifying the signature and verifying the claims of the token.

How to use JWT in web application? ›

Upon successful login, the server creates a JWT containing user information and a signature to verify its authenticity. The server sends the JWT to the client. Then, each subsequent request from the client includes the JWT. The server validates the token's signature to ensure it hasn't been tampered with.

What is a JWT app? ›

A JWT app is a type of server-to-server authenticated app, which allows users as well as other apps to consume its services. When authenticating to the Zoom API, a JWT should be generated uniquely by a server-side application and included as a Bearer Token in the header of each request.

What is the authentication flow of a mobile app? ›

The authentication flow is as follows: The app sends a request with the user's credentials to the backend server. The server verifies the credentials. If the credentials are valid, the server creates a new session along with a random session ID.

Which authentication mechanism is recommended for mobile app authentication? ›

The best methods to consider are password-based authentication, MFA and biometric authentication.

Can I decode a JWT token? ›

Decoding a JWT token involves verifying the signature and decoding the payload. The signature is generated using a secret key known only to the token issuer. When decoding a JWT token, only the payload is decoded, which contains the actual data and is not encrypted.

What is the difference between JWT and OAuth? ›

JWT is mainly used for APIs while OAuth can be used for web, browser, API, and various apps or resources. JWT token vs oauth token: JWT defines a token format while OAuth deals in defining authorization protocols. JWT is simple and easy to learn from the initial stage while OAuth is complex.

What is the difference between JWT and token? ›

The token is stored on the server-side and used to authenticate subsequent requests from the same user. In contrast, client-side authentication using JWT involves issuing a signed token to the client upon successful login, which is then stored on the client-side and sent back to the server with each subsequent request.

Can we use JWT for web applications? ›

JWT stands for JSON Web Token. It is an open standard (RFC 7519) for securely transmitting information between parties as a JSON object. JWTs are commonly used for authentication and authorization in web applications and APIs. This information can be verified and trusted because it is digitally signed.

How to call API with JWT token? ›

IBM® z/OS® Connect provides three ways of calling an API secured with a JWT: Use the OAuth 2.0 support when the request is part of an OAuth 2.0 flow. If a JWT is returned as an access token from an authentication server, the access token is sent in an API request by IBM z/OS Connect in the HTTP authorization header.

How does JWT authentication work in REST API? ›

Client sends the stored JWT in an Authorization header for every request to the service provider. For each request, the service provider takes the JWT from the Authorization header and decrypts it, if needed, validates the signature, and if everything is OK, extracts the user data and permissions.

Why use JWT instead of token? ›

There are benefits to using JWTs when compared to simple web tokens (SWTs) and SAML tokens. More compact: JSON is less verbose than XML, so when it is encoded, a JWT is smaller than a SAML token. This makes JWT a good choice to be passed in HTML and HTTP environments.

How should JWT be used? ›

JWTs can be used as access tokens or ID tokens, or sometimes for other purposes. It is thus important to differentiate the types of tokens. When validating JWTs, always make sure that they are used as intended. E.g., a resource server should not accept an ID token JWT as an access token.

What are the three parts of a JWT token? ›

Anatomy of a JWT

Figure 1 shows that a JWT consists of three parts: a header, payload, and signature. The header typically consists of two parts: the type of the token, which is JWT, and the algorithm that is used, such as HMAC SHA256 or RSA SHA256. It is Base64Url encoded to form the first part of the JWT.

What is JWT in IOS? ›

JSON Web Token (JWT) is an open standard (RFC 7519) that defines a way to securely transmit information. The App Store Connect API requires JWTs to authorize each API request. You create the token, and sign it with the private key you downloaded from App Store Connect.

Can we use JWT for authentication? ›

To authenticate a user, a client application must send a JSON Web Token (JWT) in the authorization header of the HTTP request to your backend API. API Gateway validates the token on behalf of your API, so you don't have to add any code in your API to process the authentication.

Can JWT be used for soap? ›

JWT authentication is supported for both REST and SOAP APIs. API Gateway does not support Base64 encoded JWT tokens. This describes the high level workflow for the scenario where API Gateway can generate the JSON Web Token itself.

Top Articles
Latest Posts
Article information

Author: Aracelis Kilback

Last Updated:

Views: 5970

Rating: 4.3 / 5 (44 voted)

Reviews: 83% of readers found this page helpful

Author information

Name: Aracelis Kilback

Birthday: 1994-11-22

Address: Apt. 895 30151 Green Plain, Lake Mariela, RI 98141

Phone: +5992291857476

Job: Legal Officer

Hobby: LARPing, role-playing games, Slacklining, Reading, Inline skating, Brazilian jiu-jitsu, Dance

Introduction: My name is Aracelis Kilback, I am a nice, gentle, agreeable, joyous, attractive, combative, gifted person who loves writing and wants to share my knowledge and understanding with you.