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 (RFC 7521). 058 * <li>JSON Web Token (JWT) Bearer Token Profiles for OAuth 2.0 (RFC 7523)/ 059 * </ul> 060 */ 061@Immutable 062public final class PrivateKeyJWT extends JWTAuthentication { 063 064 065 /** 066 * Gets the set of supported signature JSON Web Algorithms (JWAs) by 067 * this implementation of private key JSON Web Token (JWT) 068 * authentication. 069 * 070 * @return The set of supported JSON Web Algorithms (JWAs). 071 */ 072 public static Set<JWSAlgorithm> getSupportedJWAs() { 073 074 Set<JWSAlgorithm> supported = new HashSet<>(); 075 076 supported.add(JWSAlgorithm.RS256); 077 supported.add(JWSAlgorithm.RS384); 078 supported.add(JWSAlgorithm.RS512); 079 080 supported.add(JWSAlgorithm.PS256); 081 supported.add(JWSAlgorithm.PS384); 082 supported.add(JWSAlgorithm.PS512); 083 084 supported.add(JWSAlgorithm.ES256); 085 supported.add(JWSAlgorithm.ES384); 086 supported.add(JWSAlgorithm.ES512); 087 088 return Collections.unmodifiableSet(supported); 089 } 090 091 092 /** 093 * Creates a private key JWT authentication. 094 * 095 * @param clientAssertion The client assertion, corresponding to the 096 * {@code client_assertion} parameter, as a 097 * supported RSA or ECDSA-signed JWT. Must be 098 * signed and not {@code null}. 099 */ 100 public PrivateKeyJWT(final SignedJWT clientAssertion) { 101 102 super(ClientAuthenticationMethod.PRIVATE_KEY_JWT, clientAssertion); 103 104 if (! getSupportedJWAs().contains(clientAssertion.getHeader().getAlgorithm())) 105 throw new IllegalArgumentException("The client assertion JWT must be RSA or ECDSA-signed (RS256, RS384, RS512, PS256, PS384, PS512, ES256, ES384 or ES512)"); 106 } 107 108 109 /** 110 * Parses the specified parameters map for a private key JSON Web Token 111 * (JWT) authentication. Note that the parameters must not be 112 * {@code application/x-www-form-urlencoded} encoded. 113 * 114 * @param params The parameters map to parse. The private key JSON 115 * Web Token (JWT) parameters must be keyed under 116 * "client_assertion" and "client_assertion_type". The 117 * map must not be {@code null}. 118 * 119 * @return The private key JSON Web Token (JWT) authentication. 120 * 121 * @throws ParseException If the parameters map couldn't be parsed to a 122 * private key JSON Web Token (JWT) 123 * authentication. 124 */ 125 public static PrivateKeyJWT parse(final Map<String,String> params) 126 throws ParseException { 127 128 JWTAuthentication.ensureClientAssertionType(params); 129 130 SignedJWT clientAssertion = JWTAuthentication.parseClientAssertion(params); 131 132 PrivateKeyJWT privateKeyJWT; 133 134 try { 135 privateKeyJWT = new PrivateKeyJWT(clientAssertion); 136 137 }catch (IllegalArgumentException e) { 138 139 throw new ParseException(e.getMessage(), e); 140 } 141 142 // Check that the top level client_id matches the assertion subject + issuer 143 144 ClientID clientID = JWTAuthentication.parseClientID(params); 145 146 if (clientID != null) { 147 148 if (! clientID.equals(privateKeyJWT.getClientID())) 149 throw new ParseException("The client identifier doesn't match the client assertion subject / issuer"); 150 } 151 152 return privateKeyJWT; 153 } 154 155 156 /** 157 * Parses a private key JSON Web Token (JWT) authentication from the 158 * specified {@code application/x-www-form-urlencoded} encoded 159 * parameters string. 160 * 161 * @param paramsString The parameters string to parse. The private key 162 * JSON Web Token (JWT) parameters must be keyed 163 * under "client_assertion" and 164 * "client_assertion_type". The string must not be 165 * {@code null}. 166 * 167 * @return The private key JSON Web Token (JWT) authentication. 168 * 169 * @throws ParseException If the parameters string couldn't be parsed 170 * to a private key JSON Web Token (JWT) 171 * authentication. 172 */ 173 public static PrivateKeyJWT parse(final String paramsString) 174 throws ParseException { 175 176 Map<String,String> params = URLUtils.parseParameters(paramsString); 177 178 return parse(params); 179 } 180 181 182 /** 183 * Parses the specified HTTP POST request for a private key JSON Web 184 * Token (JWT) authentication. 185 * 186 * @param httpRequest The HTTP POST request to parse. Must not be 187 * {@code null} and must contain a valid 188 * {@code application/x-www-form-urlencoded} encoded 189 * parameters string in the entity body. The private 190 * key JSON Web Token (JWT) parameters must be 191 * keyed under "client_assertion" and 192 * "client_assertion_type". 193 * 194 * @return The private key JSON Web Token (JWT) authentication. 195 * 196 * @throws ParseException If the HTTP request header couldn't be parsed 197 * to a private key JSON Web Token (JWT) 198 * authentication. 199 */ 200 public static PrivateKeyJWT parse(final HTTPRequest httpRequest) 201 throws ParseException { 202 203 httpRequest.ensureMethod(HTTPRequest.Method.POST); 204 httpRequest.ensureContentType(CommonContentTypes.APPLICATION_URLENCODED); 205 206 return parse(httpRequest.getQueryParameters()); 207 } 208}