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&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 (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}