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&amp;
048 * code=i1WsRn1uB1&amp;
049 * client_id=s6BhdRkqt3&amp;
050 * client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer&amp;
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}