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 com.nimbusds.jose.JWSObject;
010import net.jcip.annotations.Immutable;
011
012import com.nimbusds.jose.JWSAlgorithm;
013import com.nimbusds.jwt.SignedJWT;
014
015import com.nimbusds.oauth2.sdk.ParseException;
016import com.nimbusds.oauth2.sdk.id.ClientID;
017import com.nimbusds.oauth2.sdk.http.CommonContentTypes;
018import com.nimbusds.oauth2.sdk.http.HTTPRequest;
019import com.nimbusds.oauth2.sdk.util.URLUtils;
020
021
022/**
023 * Client secret JWT authentication at the Token endpoint. Implements
024 * {@link ClientAuthenticationMethod#CLIENT_SECRET_JWT}. This class is 
025 * immutable.
026 *
027 * <p>Supported signature JSON Web Algorithms (JWAs) by this implementation:
028 *
029 * <ul>
030 *     <li>HS256
031 *     <li>HS384
032 *     <li>HS512
033 * </ul>
034 *
035 * <p>Related specifications:
036 *
037 * <ul>
038 *     <li>Assertion Framework for OAuth 2.0 (draft-ietf-oauth-assertions-06)
039 *     <li>JSON Web Token (JWT) Bearer Token Profiles for OAuth 2.0 
040 *         (draft-ietf-oauth-jwt-bearer-06).
041 * </ul>
042 *
043 * @author Vladimir Dzhuvinov
044 */
045@Immutable
046public final class ClientSecretJWT extends JWTAuthentication {
047
048
049        /**
050         * Gets the set of supported signature JSON Web Algorithms (JWAs) by 
051         * this implementation of client secret JSON Web Token (JWT) 
052         * authentication.
053         *
054         * @return The set of supported JSON Web Algorithms (JWAs).
055         */
056        public static Set<JWSAlgorithm> getSupportedJWAs() {
057        
058                Set<JWSAlgorithm> supported = new HashSet<JWSAlgorithm>();
059                
060                supported.add(JWSAlgorithm.HS256);
061                supported.add(JWSAlgorithm.HS384);
062                supported.add(JWSAlgorithm.HS512);
063                
064                return Collections.unmodifiableSet(supported);
065        }
066
067
068        /**
069         * Creates a new client secret JWT authentication.
070         *
071         * @param clientAssertion The client assertion, corresponding to the
072         *                        {@code client_assertion_parameter}, as a
073         *                        supported HMAC-protected JWT. Must be signed
074         *                        and not {@code null}.
075         */
076        public ClientSecretJWT(final SignedJWT clientAssertion) {
077
078                super(ClientAuthenticationMethod.CLIENT_SECRET_JWT, clientAssertion);
079
080                if (! getSupportedJWAs().contains(clientAssertion.getHeader().getAlgorithm()))
081                        throw new IllegalArgumentException("The client assertion JWT must be HMAC-signed (HS256, HS384 or HS512)");
082        }
083        
084        
085        /**
086         * Parses the specified parameters map for a client secret JSON Web 
087         * Token (JWT) authentication. Note that the parameters must not be
088         * {@code application/x-www-form-urlencoded} encoded.
089         *
090         * @param params The parameters map to parse. The client secret JSON
091         *               Web Token (JWT) parameters must be keyed under 
092         *               "client_assertion" and "client_assertion_type". The 
093         *               map must not be {@code null}.
094         *
095         * @return The client secret JSON Web Token (JWT) authentication.
096         *
097         * @throws ParseException If the parameters map couldn't be parsed to a 
098         *                        client secret JSON Web Token (JWT) 
099         *                        authentication.
100         */
101        public static ClientSecretJWT parse(final Map<String,String> params)
102                throws ParseException {
103        
104                JWTAuthentication.ensureClientAssertionType(params);
105                
106                SignedJWT clientAssertion = JWTAuthentication.parseClientAssertion(params);
107
108                ClientSecretJWT clientSecretJWT;
109
110                try {
111                        clientSecretJWT = new ClientSecretJWT(clientAssertion);
112
113                } catch (IllegalArgumentException e) {
114
115                        throw new ParseException(e.getMessage(), e);
116                }
117
118                // Check that the top level client_id matches the assertion subject + issuer
119                
120                ClientID clientID = JWTAuthentication.parseClientID(params);
121
122                if (clientID != null) {
123
124                        if (! clientID.equals(clientSecretJWT.getClientID()))
125                                throw new ParseException("The client identifier doesn't match the client assertion subject / issuer");
126                }
127
128                return clientSecretJWT;
129        }
130        
131        
132        /**
133         * Parses a client secret JSON Web Token (JWT) authentication from the 
134         * specified {@code application/x-www-form-urlencoded} encoded 
135         * parameters string.
136         *
137         * @param paramsString The parameters string to parse. The client secret
138         *                     JSON Web Token (JWT) parameters must be keyed 
139         *                     under "client_assertion" and 
140         *                     "client_assertion_type". The string must not be 
141         *                     {@code null}.
142         *
143         * @return The client secret JSON Web Token (JWT) authentication.
144         *
145         * @throws ParseException If the parameters string couldn't be parsed 
146         *                        to a client secret JSON Web Token (JWT) 
147         *                        authentication.
148         */
149        public static ClientSecretJWT parse(final String paramsString)
150                throws ParseException {
151                
152                Map<String,String> params = URLUtils.parseParameters(paramsString);
153                
154                return parse(params);
155        }
156        
157        
158        /**
159         * Parses the specified HTTP POST request for a client secret JSON Web 
160         * Token (JWT) authentication.
161         *
162         * @param httpRequest The HTTP POST request to parse. Must not be 
163         *                    {@code null} and must contain a valid 
164         *                    {@code application/x-www-form-urlencoded} encoded 
165         *                    parameters string in the entity body. The client 
166         *                    secret JSON Web Token (JWT) parameters must be 
167         *                    keyed under "client_assertion" and 
168         *                    "client_assertion_type".
169         *
170         * @return The client secret JSON Web Token (JWT) authentication.
171         *
172         * @throws ParseException If the HTTP request header couldn't be parsed
173         *                        to a client secret JSON Web Token (JWT) 
174         *                        authentication.
175         */
176        public static ClientSecretJWT parse(final HTTPRequest httpRequest)
177                throws ParseException {
178                
179                httpRequest.ensureMethod(HTTPRequest.Method.POST);
180                httpRequest.ensureContentType(CommonContentTypes.APPLICATION_URLENCODED);
181                
182                return parse(httpRequest.getQueryParameters());
183        }
184}