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}