001/*
002 * oauth2-oidc-sdk
003 *
004 * Copyright 2012-2016, Connect2id Ltd and contributors.
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use
007 * this file except in compliance with the License. You may obtain a copy of the
008 * License at
009 *
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software distributed
013 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
014 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
015 * specific language governing permissions and limitations under the License.
016 */
017
018package com.nimbusds.oauth2.sdk.auth;
019
020
021import java.util.List;
022import java.util.Map;
023import javax.security.auth.x500.X500Principal;
024
025import com.nimbusds.oauth2.sdk.ParseException;
026import com.nimbusds.oauth2.sdk.http.CommonContentTypes;
027import com.nimbusds.oauth2.sdk.http.HTTPRequest;
028import com.nimbusds.oauth2.sdk.id.ClientID;
029import com.nimbusds.oauth2.sdk.util.MultivaluedMapUtils;
030import com.nimbusds.oauth2.sdk.util.StringUtils;
031
032
033/**
034 * Base abstract class for client authentication at the Token endpoint.
035 *
036 * <p>Related specifications:
037 *
038 * <ul>
039 *     <li>OAuth 2.0 (RFC 6749), section 2.3.
040 *     <li>JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and
041 *         Authorization Grants (RFC 7523), section 2.2.
042 *     <li>OAuth 2.0 Mutual TLS Client Authentication and Certificate Bound
043 *         Access Tokens (draft-ietf-oauth-mtls-15), section 2.
044 * </ul>
045 */
046public abstract class ClientAuthentication {
047        
048        
049        /**
050         * The client authentication method.
051         */
052        private final ClientAuthenticationMethod method;
053
054
055        /**
056         * The client ID.
057         */
058        private final ClientID clientID;
059        
060        
061        /**
062         * Creates a new abstract client authentication.
063         *
064         * @param method   The client authentication method. Must not be
065         *                 {@code null}.
066         * @param clientID The client identifier. Must not be {@code null}.
067         */
068        protected ClientAuthentication(final ClientAuthenticationMethod method, final ClientID clientID) {
069        
070                if (method == null)
071                        throw new IllegalArgumentException("The client authentication method must not be null");
072                
073                this.method = method;
074
075
076                if (clientID == null)
077                        throw new IllegalArgumentException("The client identifier must not be null");
078
079                this.clientID = clientID;
080        }
081        
082        
083        /**
084         * Gets the client authentication method.
085         *
086         * @return The client authentication method.
087         */
088        public ClientAuthenticationMethod getMethod() {
089        
090                return method;
091        }
092
093
094        /**
095         * Gets the client identifier.
096         *
097         * @return The client identifier.
098         */
099        public ClientID getClientID() {
100
101                return clientID;
102        }
103        
104        
105        /**
106         * Parses the specified HTTP request for a supported client 
107         * authentication (see {@link ClientAuthenticationMethod}). This method
108         * is intended to aid parsing of authenticated 
109         * {@link com.nimbusds.oauth2.sdk.TokenRequest}s.
110         *
111         * @param httpRequest The HTTP request to parse. Must not be 
112         *                    {@code null}.
113         *
114         * @return The client authentication method, {@code null} if none or 
115         *         the method is not supported.
116         *
117         * @throws ParseException If the inferred client authentication 
118         *                        couldn't be parsed.
119         */
120        public static ClientAuthentication parse(final HTTPRequest httpRequest)
121                throws ParseException {
122        
123                // Check for client secret basic
124                if (httpRequest.getAuthorization() != null && 
125                    httpRequest.getAuthorization().startsWith("Basic")) {
126                        
127                        return ClientSecretBasic.parse(httpRequest);
128                }
129                
130                // The other methods require HTTP POST with URL-encoded params
131                if (httpRequest.getMethod() != HTTPRequest.Method.POST &&
132                    ! httpRequest.getContentType().match(CommonContentTypes.APPLICATION_URLENCODED)) {
133                        return null; // no auth
134                }
135                
136                Map<String,List<String>> params = httpRequest.getQueryParameters();
137                
138                // We have client secret post
139                if (StringUtils.isNotBlank(MultivaluedMapUtils.getFirstValue(params, "client_id")) && StringUtils.isNotBlank(MultivaluedMapUtils.getFirstValue(params, "client_secret"))) {
140                        return ClientSecretPost.parse(httpRequest);
141                }
142                
143                // Do we have a signed JWT assertion?
144                if (StringUtils.isNotBlank(MultivaluedMapUtils.getFirstValue(params, "client_assertion")) && StringUtils.isNotBlank(MultivaluedMapUtils.getFirstValue(params, "client_assertion_type"))) {
145                        return JWTAuthentication.parse(httpRequest);
146                }
147                
148                // Client TLS?
149                if (httpRequest.getClientX509Certificate() != null && StringUtils.isNotBlank(MultivaluedMapUtils.getFirstValue(params, "client_id"))) {
150                        
151                        // Check for self-issued first (not for self-signed (too expensive in terms of CPU time)
152                        
153                        X500Principal issuer = httpRequest.getClientX509Certificate().getIssuerX500Principal();
154                        X500Principal subject = httpRequest.getClientX509Certificate().getSubjectX500Principal();
155                        
156                        if (issuer != null && issuer.equals(subject)) {
157                                // Additional checks
158                                if (httpRequest.getClientX509CertificateRootDN() != null) {
159                                        // If TLS proxy set issuer header it must match the certificate's
160                                        if (! httpRequest.getClientX509CertificateRootDN().equalsIgnoreCase(issuer.toString())) {
161                                                throw new ParseException("Client X.509 certificate issuer DN doesn't match HTTP request metadata");
162                                        }
163                                }
164                                if (httpRequest.getClientX509CertificateSubjectDN() != null) {
165                                        // If TLS proxy set subject header it must match the certificate's
166                                        if (! httpRequest.getClientX509CertificateSubjectDN().equalsIgnoreCase(subject.toString())) {
167                                                throw new ParseException("Client X.509 certificate subject DN doesn't match HTTP request metadata");
168                                        }
169                                }
170                                
171                                // Self-issued (assumes self-signed)
172                                return SelfSignedTLSClientAuthentication.parse(httpRequest);
173                        } else {
174                                // PKI bound
175                                return PKITLSClientAuthentication.parse(httpRequest);
176                        }
177                }
178                
179                return null; // no auth
180        }
181        
182        
183        /**
184         * Applies the authentication to the specified HTTP request by setting 
185         * its Authorization header and/or POST entity-body parameters 
186         * (according to the implemented client authentication method).
187         *
188         * @param httpRequest The HTTP request. Must not be {@code null}.
189         */
190        public abstract void applyTo(final HTTPRequest httpRequest);
191}