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