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 com.nimbusds.common.contenttype.ContentType;
022import com.nimbusds.oauth2.sdk.ParseException;
023import com.nimbusds.oauth2.sdk.http.HTTPRequest;
024import com.nimbusds.oauth2.sdk.id.ClientID;
025import com.nimbusds.oauth2.sdk.util.MultivaluedMapUtils;
026import com.nimbusds.oauth2.sdk.util.StringUtils;
027
028import javax.security.auth.x500.X500Principal;
029import java.util.List;
030import java.util.Map;
031import java.util.Objects;
032import java.util.Set;
033
034
035/**
036 * Base abstract class for client authentication at the Token endpoint.
037 *
038 * <p>Related specifications:
039 *
040 * <ul>
041 *     <li>OAuth 2.0 (RFC 6749)
042 *     <li>JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and
043 *         Authorization Grants (RFC 7523)
044 *     <li>OAuth 2.0 Mutual TLS Client Authentication and Certificate Bound
045 *         Access Tokens (RFC 8705)
046 * </ul>
047 */
048public abstract class ClientAuthentication {
049        
050        
051        /**
052         * The client authentication method.
053         */
054        private final ClientAuthenticationMethod method;
055
056
057        /**
058         * The client ID.
059         */
060        private final ClientID clientID;
061        
062        
063        /**
064         * Creates a new abstract client authentication.
065         *
066         * @param method   The client authentication method. Must not be
067         *                 {@code null}.
068         * @param clientID The client identifier. Must not be {@code null}.
069         */
070        protected ClientAuthentication(final ClientAuthenticationMethod method, final ClientID clientID) {
071        
072                this.method = Objects.requireNonNull(method);
073                this.clientID = Objects.requireNonNull(clientID);
074        }
075        
076        
077        /**
078         * Returns the client authentication method.
079         *
080         * @return The client authentication method.
081         */
082        public ClientAuthenticationMethod getMethod() {
083        
084                return method;
085        }
086
087
088        /**
089         * Returns the client identifier.
090         *
091         * @return The client identifier.
092         */
093        public ClientID getClientID() {
094
095                return clientID;
096        }
097        
098        
099        /**
100         * Returns the name of the form parameters, if such are used by the
101         * authentication method.
102         *
103         * @return The form parameter names, empty set if none.
104         */
105        public abstract Set<String> getFormParameterNames();
106        
107        
108        /**
109         * Parses the specified HTTP request for a supported client 
110         * authentication (see {@link ClientAuthenticationMethod}). This method
111         * is intended to aid parsing of authenticated 
112         * {@link com.nimbusds.oauth2.sdk.TokenRequest}s.
113         *
114         * @param httpRequest The HTTP request to parse. Must not be 
115         *                    {@code null}.
116         *
117         * @return The client authentication method, {@code null} if none or 
118         *         the method is not supported.
119         *
120         * @throws ParseException If the inferred client authentication 
121         *                        couldn't be parsed.
122         */
123        public static ClientAuthentication parse(final HTTPRequest httpRequest)
124                throws ParseException {
125        
126                // Check for client secret basic
127                if (httpRequest.getAuthorization() != null && 
128                    httpRequest.getAuthorization().startsWith("Basic")) {
129                        
130                        return ClientSecretBasic.parse(httpRequest);
131                }
132                
133                // The other methods require HTTP POST with URL-encoded params
134                if (httpRequest.getMethod() != HTTPRequest.Method.POST &&
135                    ! httpRequest.getEntityContentType().matches(ContentType.APPLICATION_URLENCODED)) {
136                        return null; // no auth
137                }
138                
139                Map<String,List<String>> params = httpRequest.getBodyAsFormParameters();
140                
141                // We have client secret post
142                if (StringUtils.isNotBlank(MultivaluedMapUtils.getFirstValue(params, "client_id")) && StringUtils.isNotBlank(MultivaluedMapUtils.getFirstValue(params, "client_secret"))) {
143                        return ClientSecretPost.parse(httpRequest);
144                }
145                
146                // Do we have a signed JWT assertion?
147                if (StringUtils.isNotBlank(MultivaluedMapUtils.getFirstValue(params, "client_assertion")) && StringUtils.isNotBlank(MultivaluedMapUtils.getFirstValue(params, "client_assertion_type"))) {
148                        return JWTAuthentication.parse(httpRequest);
149                }
150                
151                // Client TLS?
152                if (httpRequest.getClientX509Certificate() != null && StringUtils.isNotBlank(MultivaluedMapUtils.getFirstValue(params, "client_id"))) {
153                        
154                        // Check for self-issued first (not for self-signed (too expensive in terms of CPU time)
155                        
156                        X500Principal issuer = httpRequest.getClientX509Certificate().getIssuerX500Principal();
157                        X500Principal subject = httpRequest.getClientX509Certificate().getSubjectX500Principal();
158                        
159                        if (issuer != null && issuer.equals(subject)) {
160                                // Additional checks
161                                if (httpRequest.getClientX509CertificateRootDN() != null) {
162                                        // If TLS proxy set issuer header it must match the certificate's
163                                        if (! httpRequest.getClientX509CertificateRootDN().equalsIgnoreCase(issuer.toString())) {
164                                                throw new ParseException("Client X.509 certificate issuer DN doesn't match HTTP request metadata");
165                                        }
166                                }
167                                if (httpRequest.getClientX509CertificateSubjectDN() != null) {
168                                        // If TLS proxy set subject header it must match the certificate's
169                                        if (! httpRequest.getClientX509CertificateSubjectDN().equalsIgnoreCase(subject.toString())) {
170                                                throw new ParseException("Client X.509 certificate subject DN doesn't match HTTP request metadata");
171                                        }
172                                }
173                                
174                                // Self-issued (assumes self-signed)
175                                return SelfSignedTLSClientAuthentication.parse(httpRequest);
176                        } else {
177                                // PKI bound
178                                return PKITLSClientAuthentication.parse(httpRequest);
179                        }
180                }
181                
182                return null; // no auth
183        }
184        
185        
186        /**
187         * Applies the authentication to the specified HTTP request by setting 
188         * its Authorization header and/or POST entity-body parameters 
189         * (according to the implemented client authentication method).
190         *
191         * @param httpRequest The HTTP request. Must not be {@code null}.
192         */
193        public abstract void applyTo(final HTTPRequest httpRequest);
194}