001package com.nimbusds.oauth2.sdk.auth; 002 003 004import java.util.Collections; 005import java.util.HashSet; 006import java.util.Map; 007import java.util.Set; 008 009import net.jcip.annotations.Immutable; 010 011import com.nimbusds.jose.JWSAlgorithm; 012import com.nimbusds.jwt.SignedJWT; 013 014import com.nimbusds.oauth2.sdk.ParseException; 015import com.nimbusds.oauth2.sdk.id.ClientID; 016import com.nimbusds.oauth2.sdk.http.CommonContentTypes; 017import com.nimbusds.oauth2.sdk.http.HTTPRequest; 018import com.nimbusds.oauth2.sdk.util.URLUtils; 019 020 021/** 022 * Private key JWT authentication at the Token endpoint. Implements 023 * {@link ClientAuthenticationMethod#PRIVATE_KEY_JWT}. 024 * 025 * <p>Supported signature JSON Web Algorithms (JWAs) by this implementation: 026 * 027 * <ul> 028 * <li>RS256 029 * <li>RS384 030 * <li>RS512 031 * <li>PS256 032 * <li>PS384 033 * <li>PS512 034 * <li>ES256 035 * <li>ES384 036 * <li>ES512 037 * </ul> 038 * 039 * <p>Example {@link com.nimbusds.oauth2.sdk.TokenRequest} with private key JWT 040 * authentication: 041 * 042 * <pre> 043 * POST /token HTTP/1.1 044 * Host: server.example.com 045 * Content-Type: application/x-www-form-urlencoded 046 * 047 * grant_type=authorization_code& 048 * code=i1WsRn1uB1& 049 * client_id=s6BhdRkqt3& 050 * client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer& 051 * client_assertion=PHNhbWxwOl...[omitted for brevity]...ZT 052 * </pre> 053 * 054 * <p>Related specifications: 055 * 056 * <ul> 057 * <li>Assertion Framework for OAuth 2.0 (draft-ietf-oauth-assertions-06) 058 * <li>JSON Web Token (JWT) Bearer Token Profiles for OAuth 2.0 059 * (draft-ietf-oauth-jwt-bearer-06). 060 * </ul> 061 */ 062@Immutable 063public final class PrivateKeyJWT extends JWTAuthentication { 064 065 066 /** 067 * Gets the set of supported signature JSON Web Algorithms (JWAs) by 068 * this implementation of private key JSON Web Token (JWT) 069 * authentication. 070 * 071 * @return The set of supported JSON Web Algorithms (JWAs). 072 */ 073 public static Set<JWSAlgorithm> getSupportedJWAs() { 074 075 Set<JWSAlgorithm> supported = new HashSet<JWSAlgorithm>(); 076 077 supported.add(JWSAlgorithm.RS256); 078 supported.add(JWSAlgorithm.RS384); 079 supported.add(JWSAlgorithm.RS512); 080 081 supported.add(JWSAlgorithm.PS256); 082 supported.add(JWSAlgorithm.PS384); 083 supported.add(JWSAlgorithm.PS512); 084 085 supported.add(JWSAlgorithm.ES256); 086 supported.add(JWSAlgorithm.ES384); 087 supported.add(JWSAlgorithm.ES512); 088 089 return Collections.unmodifiableSet(supported); 090 } 091 092 093 /** 094 * Creates a private key JWT authentication. 095 * 096 * @param clientAssertion The client assertion, corresponding to the 097 * {@code client_assertion} parameter, as a 098 * supported RSA or ECDSA-signed JWT. Must be 099 * signed and not {@code null}. 100 */ 101 public PrivateKeyJWT(final SignedJWT clientAssertion) { 102 103 super(ClientAuthenticationMethod.PRIVATE_KEY_JWT, clientAssertion); 104 105 if (! getSupportedJWAs().contains(clientAssertion.getHeader().getAlgorithm())) 106 throw new IllegalArgumentException("The client assertion JWT must be RSA or ECDSA-signed (RS256, RS384, RS512, PS256, PS384, PS512, ES256, ES384 or ES512)"); 107 } 108 109 110 /** 111 * Parses the specified parameters map for a private key JSON Web Token 112 * (JWT) authentication. Note that the parameters must not be 113 * {@code application/x-www-form-urlencoded} encoded. 114 * 115 * @param params The parameters map to parse. The private key JSON 116 * Web Token (JWT) parameters must be keyed under 117 * "client_assertion" and "client_assertion_type". The 118 * map must not be {@code null}. 119 * 120 * @return The private key JSON Web Token (JWT) authentication. 121 * 122 * @throws ParseException If the parameters map couldn't be parsed to a 123 * private key JSON Web Token (JWT) 124 * authentication. 125 */ 126 public static PrivateKeyJWT parse(final Map<String,String> params) 127 throws ParseException { 128 129 JWTAuthentication.ensureClientAssertionType(params); 130 131 SignedJWT clientAssertion = JWTAuthentication.parseClientAssertion(params); 132 133 PrivateKeyJWT privateKeyJWT; 134 135 try { 136 privateKeyJWT = new PrivateKeyJWT(clientAssertion); 137 138 }catch (IllegalArgumentException e) { 139 140 throw new ParseException(e.getMessage(), e); 141 } 142 143 // Check that the top level client_id matches the assertion subject + issuer 144 145 ClientID clientID = JWTAuthentication.parseClientID(params); 146 147 if (clientID != null) { 148 149 if (! clientID.equals(privateKeyJWT.getClientID())) 150 throw new ParseException("The client identifier doesn't match the client assertion subject / issuer"); 151 } 152 153 return privateKeyJWT; 154 } 155 156 157 /** 158 * Parses a private key JSON Web Token (JWT) authentication from the 159 * specified {@code application/x-www-form-urlencoded} encoded 160 * parameters string. 161 * 162 * @param paramsString The parameters string to parse. The private key 163 * JSON Web Token (JWT) parameters must be keyed 164 * under "client_assertion" and 165 * "client_assertion_type". The string must not be 166 * {@code null}. 167 * 168 * @return The private key JSON Web Token (JWT) authentication. 169 * 170 * @throws ParseException If the parameters string couldn't be parsed 171 * to a private key JSON Web Token (JWT) 172 * authentication. 173 */ 174 public static PrivateKeyJWT parse(final String paramsString) 175 throws ParseException { 176 177 Map<String,String> params = URLUtils.parseParameters(paramsString); 178 179 return parse(params); 180 } 181 182 183 /** 184 * Parses the specified HTTP POST request for a private key JSON Web 185 * Token (JWT) authentication. 186 * 187 * @param httpRequest The HTTP POST request to parse. Must not be 188 * {@code null} and must contain a valid 189 * {@code application/x-www-form-urlencoded} encoded 190 * parameters string in the entity body. The private 191 * key JSON Web Token (JWT) parameters must be 192 * keyed under "client_assertion" and 193 * "client_assertion_type". 194 * 195 * @return The private key JSON Web Token (JWT) authentication. 196 * 197 * @throws ParseException If the HTTP request header couldn't be parsed 198 * to a private key JSON Web Token (JWT) 199 * authentication. 200 */ 201 public static PrivateKeyJWT parse(final HTTPRequest httpRequest) 202 throws ParseException { 203 204 httpRequest.ensureMethod(HTTPRequest.Method.POST); 205 httpRequest.ensureContentType(CommonContentTypes.APPLICATION_URLENCODED); 206 207 return parse(httpRequest.getQueryParameters()); 208 } 209}