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