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.net.URI; 022import java.util.Collections; 023import java.util.HashSet; 024import java.util.Map; 025import java.util.Set; 026 027import net.jcip.annotations.Immutable; 028 029import com.nimbusds.jose.JOSEException; 030import com.nimbusds.jose.JWSAlgorithm; 031import com.nimbusds.jwt.SignedJWT; 032 033import com.nimbusds.oauth2.sdk.ParseException; 034import com.nimbusds.oauth2.sdk.assertions.jwt.JWTAssertionFactory; 035import com.nimbusds.oauth2.sdk.http.CommonContentTypes; 036import com.nimbusds.oauth2.sdk.http.HTTPRequest; 037import com.nimbusds.oauth2.sdk.id.Audience; 038import com.nimbusds.oauth2.sdk.id.ClientID; 039import com.nimbusds.oauth2.sdk.util.URLUtils; 040 041 042/** 043 * Client secret JWT authentication at the Token endpoint. Implements 044 * {@link ClientAuthenticationMethod#CLIENT_SECRET_JWT}. 045 * 046 * <p>Supported signature JSON Web Algorithms (JWAs) by this implementation: 047 * 048 * <ul> 049 * <li>HS256 050 * <li>HS384 051 * <li>HS512 052 * </ul> 053 * 054 * <p>Related specifications: 055 * 056 * <ul> 057 * <li>Assertion Framework for OAuth 2.0 Client Authentication and 058 * Authorization Grants (RFC 7521). 059 * <li>JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and 060 * Authorization Grants (RFC 7523). 061 * </ul> 062 */ 063@Immutable 064public final class ClientSecretJWT extends JWTAuthentication { 065 066 067 /** 068 * Returns the supported signature JSON Web Algorithms (JWAs). 069 * 070 * @return The supported JSON Web Algorithms (JWAs). 071 */ 072 public static Set<JWSAlgorithm> supportedJWAs() { 073 074 Set<JWSAlgorithm> supported = new HashSet<>(); 075 supported.addAll(JWSAlgorithm.Family.HMAC_SHA); 076 return Collections.unmodifiableSet(supported); 077 } 078 079 080 /** 081 * Creates a new client secret JWT authentication. The expiration 082 * time (exp) is set to five minutes from the current system time. 083 * Generates a default identifier (jti) for the JWT. The issued-at 084 * (iat) and not-before (nbf) claims are not set. 085 * 086 * @param clientID The client identifier. Must not be 087 * {@code null}. 088 * @param tokenEndpoint The token endpoint URI of the authorisation 089 * server. Must not be {@code null}. 090 * @param jwsAlgorithm The expected HMAC algorithm (HS256, HS384 or 091 * HS512) for the client secret JWT assertion. 092 * Must be supported and not {@code null}. 093 * @param clientSecret The client secret. Must be at least 256-bits 094 * long. 095 * 096 * @throws JOSEException If the client secret is too short, or HMAC 097 * computation failed. 098 */ 099 public ClientSecretJWT(final ClientID clientID, 100 final URI tokenEndpoint, 101 final JWSAlgorithm jwsAlgorithm, 102 final Secret clientSecret) 103 throws JOSEException { 104 105 this(JWTAssertionFactory.create( 106 new JWTAuthenticationClaimsSet(clientID, new Audience(tokenEndpoint.toString())), 107 jwsAlgorithm, 108 clientSecret)); 109 } 110 111 112 /** 113 * Creates a new client secret JWT authentication. 114 * 115 * @param clientAssertion The client assertion, corresponding to the 116 * {@code client_assertion_parameter}, as a 117 * supported HMAC-protected JWT. Must be signed 118 * and not {@code null}. 119 */ 120 public ClientSecretJWT(final SignedJWT clientAssertion) { 121 122 super(ClientAuthenticationMethod.CLIENT_SECRET_JWT, clientAssertion); 123 124 if (! JWSAlgorithm.Family.HMAC_SHA.contains(clientAssertion.getHeader().getAlgorithm())) 125 throw new IllegalArgumentException("The client assertion JWT must be HMAC-signed (HS256, HS384 or HS512)"); 126 } 127 128 129 /** 130 * Parses the specified parameters map for a client secret JSON Web 131 * Token (JWT) authentication. Note that the parameters must not be 132 * {@code application/x-www-form-urlencoded} encoded. 133 * 134 * @param params The parameters map to parse. The client secret JSON 135 * Web Token (JWT) parameters must be keyed under 136 * "client_assertion" and "client_assertion_type". The 137 * map must not be {@code null}. 138 * 139 * @return The client secret JSON Web Token (JWT) authentication. 140 * 141 * @throws ParseException If the parameters map couldn't be parsed to a 142 * client secret JSON Web Token (JWT) 143 * authentication. 144 */ 145 public static ClientSecretJWT parse(final Map<String,String> params) 146 throws ParseException { 147 148 JWTAuthentication.ensureClientAssertionType(params); 149 150 SignedJWT clientAssertion = JWTAuthentication.parseClientAssertion(params); 151 152 ClientSecretJWT clientSecretJWT; 153 154 try { 155 clientSecretJWT = new ClientSecretJWT(clientAssertion); 156 157 } catch (IllegalArgumentException e) { 158 159 throw new ParseException(e.getMessage(), e); 160 } 161 162 // Check that the top level client_id matches the assertion subject + issuer 163 164 ClientID clientID = JWTAuthentication.parseClientID(params); 165 166 if (clientID != null) { 167 168 if (! clientID.equals(clientSecretJWT.getClientID())) 169 throw new ParseException("Invalid client secret JWT authentication: The client identifier doesn't match the client assertion subject / issuer"); 170 } 171 172 return clientSecretJWT; 173 } 174 175 176 /** 177 * Parses a client secret JSON Web Token (JWT) authentication from the 178 * specified {@code application/x-www-form-urlencoded} encoded 179 * parameters string. 180 * 181 * @param paramsString The parameters string to parse. The client secret 182 * JSON Web Token (JWT) parameters must be keyed 183 * under "client_assertion" and 184 * "client_assertion_type". The string must not be 185 * {@code null}. 186 * 187 * @return The client secret JSON Web Token (JWT) authentication. 188 * 189 * @throws ParseException If the parameters string couldn't be parsed 190 * to a client secret JSON Web Token (JWT) 191 * authentication. 192 */ 193 public static ClientSecretJWT parse(final String paramsString) 194 throws ParseException { 195 196 Map<String,String> params = URLUtils.parseParameters(paramsString); 197 198 return parse(params); 199 } 200 201 202 /** 203 * Parses the specified HTTP POST request for a client secret JSON Web 204 * Token (JWT) authentication. 205 * 206 * @param httpRequest The HTTP POST request to parse. Must not be 207 * {@code null} and must contain a valid 208 * {@code application/x-www-form-urlencoded} encoded 209 * parameters string in the entity body. The client 210 * secret JSON Web Token (JWT) parameters must be 211 * keyed under "client_assertion" and 212 * "client_assertion_type". 213 * 214 * @return The client secret JSON Web Token (JWT) authentication. 215 * 216 * @throws ParseException If the HTTP request header couldn't be parsed 217 * to a client secret JSON Web Token (JWT) 218 * authentication. 219 */ 220 public static ClientSecretJWT parse(final HTTPRequest httpRequest) 221 throws ParseException { 222 223 httpRequest.ensureMethod(HTTPRequest.Method.POST); 224 httpRequest.ensureContentType(CommonContentTypes.APPLICATION_URLENCODED); 225 226 return parse(httpRequest.getQueryParameters()); 227 } 228}