001package com.nimbusds.oauth2.sdk; 002 003 004import java.util.*; 005 006import net.jcip.annotations.Immutable; 007 008import com.nimbusds.jose.*; 009import com.nimbusds.jwt.*; 010 011 012/** 013 * JWT bearer grant. Used in access token requests with a JSON Web Token (JWT), 014 * such an OpenID Connect ID token. 015 * 016 * <p>Related specifications: 017 * 018 * <ul> 019 * <li>Assertion Framework for OAuth 2.0 Client Authentication and 020 * Authorization Grants (RFC 7521), section 4.1. 021 * <li>JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and 022 * Authorization Grants (RFC 7523), section-2.1. 023 * </ul> 024 */ 025@Immutable 026public class JWTBearerGrant extends AssertionGrant { 027 028 029 /** 030 * The grant type. 031 */ 032 public static final GrantType GRANT_TYPE = GrantType.JWT_BEARER; 033 034 035 /** 036 * The JWT assertion. 037 */ 038 private final JWT assertion; 039 040 041 /** 042 * Creates a new signed JSON Web Token (JWT) bearer assertion grant. 043 * 044 * @param assertion The signed JSON Web Token (JWT) assertion. Must not 045 * be in a unsigned state or {@code null}. The JWT 046 * claims are not validated for compliance with the 047 * standard. 048 */ 049 public JWTBearerGrant(final SignedJWT assertion) { 050 051 super(GRANT_TYPE); 052 053 if (assertion.getState().equals(JWSObject.State.UNSIGNED)) 054 throw new IllegalArgumentException("The JWT assertion must not be in a unsigned state"); 055 056 this.assertion = assertion; 057 } 058 059 060 /** 061 * Creates a new signed and encrypted JSON Web Token (JWT) bearer 062 * assertion grant. 063 * 064 * @param assertion The signed and encrypted JSON Web Token (JWT) 065 * assertion. Must not be in a unencrypted state or 066 * {@code null}. The JWT claims are not validated for 067 * compliance with the standard. 068 */ 069 public JWTBearerGrant(final EncryptedJWT assertion) { 070 071 super(GRANT_TYPE); 072 073 if (assertion.getState().equals(JWEObject.State.UNENCRYPTED)) 074 throw new IllegalArgumentException("The JWT assertion must not be in a unencrypted state"); 075 076 this.assertion = assertion; 077 } 078 079 080 /** 081 * Gets the JSON Web Token (JWT) bearer assertion. 082 * 083 * @return The signed or signed and encrypted JWT bearer assertion. 084 */ 085 public JWT getJWTAssertion() { 086 087 return assertion; 088 } 089 090 091 @Override 092 public String getAssertion() { 093 094 return assertion.serialize(); 095 } 096 097 098 @Override 099 public Map<String,String> toParameters() { 100 101 Map<String,String> params = new LinkedHashMap<>(); 102 params.put("grant_type", GRANT_TYPE.getValue()); 103 params.put("assertion", assertion.serialize()); 104 return params; 105 } 106 107 108 /** 109 * Parses a JWT bearer grant from the specified parameters. The JWT 110 * claims are not validated for compliance with the standard. 111 * 112 * <p>Example: 113 * 114 * <pre> 115 * grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer 116 * &assertion=eyJhbGciOiJFUzI1NiJ9.eyJpc3Mi[...omitted for brevity...]. 117 * J9l-ZhwP[...omitted for brevity...] 118 * </pre> 119 * 120 * @param params The parameters. 121 * 122 * @return The JWT bearer grant. 123 * 124 * @throws ParseException If parsing failed. 125 */ 126 public static JWTBearerGrant parse(final Map<String,String> params) 127 throws ParseException { 128 129 // Parse grant type 130 String grantTypeString = params.get("grant_type"); 131 132 if (grantTypeString == null) 133 throw new ParseException("Missing \"grant_type\" parameter", OAuth2Error.INVALID_REQUEST); 134 135 if (! GrantType.parse(grantTypeString).equals(GRANT_TYPE)) 136 throw new ParseException("The \"grant_type\" must be " + GRANT_TYPE, OAuth2Error.UNSUPPORTED_GRANT_TYPE); 137 138 // Parse JWT assertion 139 String assertionString = params.get("assertion"); 140 141 if (assertionString == null || assertionString.trim().isEmpty()) 142 throw new ParseException("Missing or empty \"assertion\" parameter", OAuth2Error.INVALID_REQUEST); 143 144 final JWT assertion; 145 146 try { 147 assertion = JWTParser.parse(assertionString); 148 } catch (java.text.ParseException e) { 149 throw new ParseException("The \"assertion\" is not a JWT: " + e.getMessage(), OAuth2Error.INVALID_REQUEST, e); 150 } 151 152 if (assertion instanceof SignedJWT) { 153 return new JWTBearerGrant((SignedJWT)assertion); 154 } else if (assertion instanceof EncryptedJWT) { 155 return new JWTBearerGrant((EncryptedJWT)assertion); 156 } else { 157 throw new ParseException("The JWT assertion must not be unsecured (plain)", OAuth2Error.INVALID_REQUEST); 158 } 159 } 160}