keycloak-service
公開メンバ関数 | 静的公開変数類 | 静的非公開変数類 | 全メンバ一覧
org.keycloak.authentication.authenticators.client.JWTClientSecretAuthenticator クラス
org.keycloak.authentication.authenticators.client.JWTClientSecretAuthenticator の継承関係図
Inheritance graph
org.keycloak.authentication.authenticators.client.JWTClientSecretAuthenticator 連携図
Collaboration graph

公開メンバ関数

void authenticateClient (ClientAuthenticationFlowContext context)
 
boolean isConfigurable ()
 
List< ProviderConfigProperty > getConfigPropertiesPerClient ()
 
Map< String, Object > getAdapterConfiguration (ClientModel client)
 
Set< String > getProtocolAuthenticatorMethods (String loginProtocol)
 
String getId ()
 
String getDisplayType ()
 
Requirement [] getRequirementChoices ()
 
String getHelpText ()
 
List< ProviderConfigProperty > getConfigProperties ()
 
ClientAuthenticator create ()
 
ClientAuthenticator create (KeycloakSession session)
 
void close ()
 
void init (Config.Scope config)
 
void postInit (KeycloakSessionFactory factory)
 
boolean isUserSetupAllowed ()
 
String getReferenceCategory ()
 

静的公開変数類

static final String PROVIDER_ID = "client-secret-jwt"
 
static final AuthenticationExecutionModel.Requirement [] REQUIREMENT_CHOICES
 

静的非公開変数類

static final Logger logger = Logger.getLogger(JWTClientSecretAuthenticator.class)
 

詳解

Client authentication based on JWT signed by client secret instead of private key . See specs for more details.

This is server side, which verifies JWT from client_assertion parameter, where the assertion was created on adapter side by org.keycloak.adapters.authentication.JWTClientSecretCredentialsProvider

著者
Takashi Norimatsu

関数詳解

◆ authenticateClient()

void org.keycloak.authentication.authenticators.client.JWTClientSecretAuthenticator.authenticateClient ( ClientAuthenticationFlowContext  context)
inline
55  {
56  MultivaluedMap<String, String> params = context.getHttpRequest().getDecodedFormParameters();
57 
58  String clientAssertionType = params.getFirst(OAuth2Constants.CLIENT_ASSERTION_TYPE);
59  String clientAssertion = params.getFirst(OAuth2Constants.CLIENT_ASSERTION);
60 
61  if (clientAssertionType == null) {
62  Response challengeResponse = ClientAuthUtil.errorResponse(Response.Status.BAD_REQUEST.getStatusCode(), "invalid_client", "Parameter client_assertion_type is missing");
63  context.challenge(challengeResponse);
64  return;
65  }
66 
67  if (!clientAssertionType.equals(OAuth2Constants.CLIENT_ASSERTION_TYPE_JWT)) {
68  Response challengeResponse = ClientAuthUtil.errorResponse(Response.Status.BAD_REQUEST.getStatusCode(), "invalid_client", "Parameter client_assertion_type has value '"
69  + clientAssertionType + "' but expected is '" + OAuth2Constants.CLIENT_ASSERTION_TYPE_JWT + "'");
70  context.challenge(challengeResponse);
71  return;
72  }
73 
74  if (clientAssertion == null) {
75  Response challengeResponse = ClientAuthUtil.errorResponse(Response.Status.BAD_REQUEST.getStatusCode(), "invalid_client", "client_assertion parameter missing");
76  context.failure(AuthenticationFlowError.INVALID_CLIENT_CREDENTIALS, challengeResponse);
77  return;
78  }
79 
80  try {
81  JWSInput jws = new JWSInput(clientAssertion);
82  JsonWebToken token = jws.readJsonContent(JsonWebToken.class);
83 
84  RealmModel realm = context.getRealm();
85  String clientId = token.getSubject();
86  if (clientId == null) {
87  throw new RuntimeException("Can't identify client. Issuer missing on JWT token");
88  }
89 
90  context.getEvent().client(clientId);
91  ClientModel client = realm.getClientByClientId(clientId);
92  if (client == null) {
93  context.failure(AuthenticationFlowError.CLIENT_NOT_FOUND, null);
94  return;
95  } else {
96  context.setClient(client);
97  }
98 
99  if (!client.isEnabled()) {
100  context.failure(AuthenticationFlowError.CLIENT_DISABLED, null);
101  return;
102  }
103 
104  String clientSecretString = client.getSecret();
105  if (clientSecretString == null) {
106  context.failure(AuthenticationFlowError.INVALID_CLIENT_CREDENTIALS, null);
107  return;
108  }
109 
110  // Get client secret and validate signature
111  // According to <a href="http://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication">OIDC's client authentication spec</a>,
112  // The HMAC (Hash-based Message Authentication Code) is calculated using the octets of the UTF-8 representation of the client_secret as the shared key.
113  // Use "HmacSHA256" consulting <a href="https://docs.oracle.com/javase/jp/8/docs/api/javax/crypto/Mac.html">java8 api</a>.
114  SecretKey clientSecret = new SecretKeySpec(clientSecretString.getBytes("UTF-8"), "HmacSHA256");
115 
116  boolean signatureValid;
117  try {
118  signatureValid = HMACProvider.verify(jws, clientSecret);
119  } catch (RuntimeException e) {
120  Throwable cause = e.getCause() != null ? e.getCause() : e;
121  throw new RuntimeException("Signature on JWT token by client secret failed validation", cause);
122  }
123  if (!signatureValid) {
124  throw new RuntimeException("Signature on JWT token by client secret failed validation");
125  }
126  // According to <a href="http://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication">OIDC's client authentication spec</a>,
127  // JWT contents and verification in client_secret_jwt is the same as in private_key_jwt
128 
129  // Allow both "issuer" or "token-endpoint" as audience
130  String issuerUrl = Urls.realmIssuer(context.getUriInfo().getBaseUri(), realm.getName());
131  String tokenUrl = OIDCLoginProtocolService.tokenUrl(context.getUriInfo().getBaseUriBuilder()).build(realm.getName()).toString();
132  if (!token.hasAudience(issuerUrl) && !token.hasAudience(tokenUrl)) {
133  throw new RuntimeException("Token audience doesn't match domain. Realm issuer is '" + issuerUrl + "' but audience from token is '" + Arrays.asList(token.getAudience()).toString() + "'");
134  }
135 
136  if (!token.isActive()) {
137  throw new RuntimeException("Token is not active");
138  }
139 
140  // KEYCLOAK-2986, token-timeout or token-expiration in keycloak.json might not be used
141  if (token.getExpiration() == 0 && token.getIssuedAt() + 10 < Time.currentTime()) {
142  throw new RuntimeException("Token is not active");
143  }
144 
145  context.success();
146  } catch (Exception e) {
147  ServicesLogger.LOGGER.errorValidatingAssertion(e);
148  Response challengeResponse = ClientAuthUtil.errorResponse(Response.Status.BAD_REQUEST.getStatusCode(), "unauthorized_client", "Client authentication with client secret signed JWT failed: " + e.getMessage());
149  context.failure(AuthenticationFlowError.INVALID_CLIENT_CREDENTIALS, challengeResponse);
150  }
151  }

◆ close()

void org.keycloak.authentication.authenticators.client.AbstractClientAuthenticator.close ( )
inlineinherited
37  {
38 
39  }

◆ create() [1/2]

ClientAuthenticator org.keycloak.authentication.authenticators.client.AbstractClientAuthenticator.create ( )
inlineinherited
32  {
33  return this;
34  }

◆ create() [2/2]

ClientAuthenticator org.keycloak.authentication.authenticators.client.AbstractClientAuthenticator.create ( KeycloakSession  session)
inlineinherited
42  {
43  return this;
44  }

◆ getAdapterConfiguration()

Map<String, Object> org.keycloak.authentication.authenticators.client.JWTClientSecretAuthenticator.getAdapterConfiguration ( ClientModel  client)
inline
165  {
166  // e.g.
167  // "credentials": {
168  // "secret-jwt": {
169  // "secret": "234234-234234-234234"
170  // }
171  // }
172  Map<String, Object> props = new HashMap<>();
173  props.put("secret", client.getSecret());
174 
175  Map<String, Object> config = new HashMap<>();
176  config.put("secret-jwt", props);
177  return config;
178  }

◆ getConfigProperties()

List<ProviderConfigProperty> org.keycloak.authentication.authenticators.client.JWTClientSecretAuthenticator.getConfigProperties ( )
inline
213  {
214  return new LinkedList<>();
215  }

◆ getConfigPropertiesPerClient()

List<ProviderConfigProperty> org.keycloak.authentication.authenticators.client.JWTClientSecretAuthenticator.getConfigPropertiesPerClient ( )
inline
159  {
160  // This impl doesn't use generic screen in admin console, but has its own screen. So no need to return anything here
161  return Collections.emptyList();
162  }

◆ getDisplayType()

String org.keycloak.authentication.authenticators.client.JWTClientSecretAuthenticator.getDisplayType ( )
inline
197  {
198  return "Signed Jwt with Client Secret";
199  }

◆ getHelpText()

String org.keycloak.authentication.authenticators.client.JWTClientSecretAuthenticator.getHelpText ( )
inline
207  {
208  return "Validates client based on signed JWT issued by client and signed with the Client Secret";
209 
210  }

◆ getId()

String org.keycloak.authentication.authenticators.client.JWTClientSecretAuthenticator.getId ( )
inline
192  {
193  return PROVIDER_ID;
194  }
static final String PROVIDER_ID
Definition: JWTClientSecretAuthenticator.java:47

◆ getProtocolAuthenticatorMethods()

Set<String> org.keycloak.authentication.authenticators.client.JWTClientSecretAuthenticator.getProtocolAuthenticatorMethods ( String  loginProtocol)
inline
181  {
182  if (loginProtocol.equals(OIDCLoginProtocol.LOGIN_PROTOCOL)) {
183  Set<String> results = new HashSet<>();
184  results.add(OIDCLoginProtocol.CLIENT_SECRET_JWT);
185  return results;
186  } else {
187  return Collections.emptySet();
188  }
189  }

◆ getReferenceCategory()

String org.keycloak.authentication.authenticators.client.AbstractClientAuthenticator.getReferenceCategory ( )
inlineinherited
62  {
63  return null;
64  }

◆ getRequirementChoices()

Requirement [] org.keycloak.authentication.authenticators.client.JWTClientSecretAuthenticator.getRequirementChoices ( )
inline
202  {
203  return REQUIREMENT_CHOICES;
204  }
static final AuthenticationExecutionModel.Requirement [] REQUIREMENT_CHOICES
Definition: JWTClientSecretAuthenticator.java:49

◆ init()

void org.keycloak.authentication.authenticators.client.AbstractClientAuthenticator.init ( Config.Scope  config)
inlineinherited
47  {
48 
49  }

◆ isConfigurable()

boolean org.keycloak.authentication.authenticators.client.JWTClientSecretAuthenticator.isConfigurable ( )
inline
154  {
155  return false;
156  }

◆ isUserSetupAllowed()

boolean org.keycloak.authentication.authenticators.client.AbstractClientAuthenticator.isUserSetupAllowed ( )
inlineinherited
57  {
58  return false;
59  }

◆ postInit()

void org.keycloak.authentication.authenticators.client.AbstractClientAuthenticator.postInit ( KeycloakSessionFactory  factory)
inlineinherited
52  {
53 
54  }

メンバ詳解

◆ logger

final Logger org.keycloak.authentication.authenticators.client.JWTClientSecretAuthenticator.logger = Logger.getLogger(JWTClientSecretAuthenticator.class)
staticprivate

◆ PROVIDER_ID

final String org.keycloak.authentication.authenticators.client.JWTClientSecretAuthenticator.PROVIDER_ID = "client-secret-jwt"
static

◆ REQUIREMENT_CHOICES

final AuthenticationExecutionModel.Requirement [] org.keycloak.authentication.authenticators.client.JWTClientSecretAuthenticator.REQUIREMENT_CHOICES
static
初期値:
= {
AuthenticationExecutionModel.Requirement.ALTERNATIVE,
AuthenticationExecutionModel.Requirement.DISABLED
}

このクラス詳解は次のファイルから抽出されました: