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

公開メンバ関数

void authenticateClient (ClientAuthenticationFlowContext context)
 
String getDisplayType ()
 
boolean isConfigurable ()
 
AuthenticationExecutionModel.Requirement [] getRequirementChoices ()
 
String getHelpText ()
 
List< ProviderConfigProperty > getConfigProperties ()
 
List< ProviderConfigProperty > getConfigPropertiesPerClient ()
 
Map< String, Object > getAdapterConfiguration (ClientModel client)
 
String getId ()
 
Set< String > getProtocolAuthenticatorMethods (String loginProtocol)
 
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-jwt"
 
static final String ATTR_PREFIX = "jwt.credential"
 
static final String CERTIFICATE_ATTR = "jwt.credential.certificate"
 
static final AuthenticationExecutionModel.Requirement [] REQUIREMENT_CHOICES
 

限定公開メンバ関数

PublicKey getSignatureValidationKey (ClientModel client, ClientAuthenticationFlowContext context, JWSInput jws)
 

詳解

Client authentication based on JWT signed by client 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.JWTClientCredentialsProvider

著者
Marek Posolda

関数詳解

◆ authenticateClient()

void org.keycloak.authentication.authenticators.client.JWTClientAuthenticator.authenticateClient ( ClientAuthenticationFlowContext  context)
inline
72  {
73  MultivaluedMap<String, String> params = context.getHttpRequest().getDecodedFormParameters();
74 
75  String clientAssertionType = params.getFirst(OAuth2Constants.CLIENT_ASSERTION_TYPE);
76  String clientAssertion = params.getFirst(OAuth2Constants.CLIENT_ASSERTION);
77 
78  if (clientAssertionType == null) {
79  Response challengeResponse = ClientAuthUtil.errorResponse(Response.Status.BAD_REQUEST.getStatusCode(), "invalid_client", "Parameter client_assertion_type is missing");
80  context.challenge(challengeResponse);
81  return;
82  }
83 
84  if (!clientAssertionType.equals(OAuth2Constants.CLIENT_ASSERTION_TYPE_JWT)) {
85  Response challengeResponse = ClientAuthUtil.errorResponse(Response.Status.BAD_REQUEST.getStatusCode(), "invalid_client", "Parameter client_assertion_type has value '"
86  + clientAssertionType + "' but expected is '" + OAuth2Constants.CLIENT_ASSERTION_TYPE_JWT + "'");
87  context.challenge(challengeResponse);
88  return;
89  }
90 
91  if (clientAssertion == null) {
92  Response challengeResponse = ClientAuthUtil.errorResponse(Response.Status.BAD_REQUEST.getStatusCode(), "invalid_client", "client_assertion parameter missing");
93  context.failure(AuthenticationFlowError.INVALID_CLIENT_CREDENTIALS, challengeResponse);
94  return;
95  }
96 
97  try {
98  JWSInput jws = new JWSInput(clientAssertion);
99  JsonWebToken token = jws.readJsonContent(JsonWebToken.class);
100 
101  RealmModel realm = context.getRealm();
102  String clientId = token.getSubject();
103  if (clientId == null) {
104  throw new RuntimeException("Can't identify client. Issuer missing on JWT token");
105  }
106 
107  context.getEvent().client(clientId);
108  ClientModel client = realm.getClientByClientId(clientId);
109  if (client == null) {
110  context.failure(AuthenticationFlowError.CLIENT_NOT_FOUND, null);
111  return;
112  } else {
113  context.setClient(client);
114  }
115 
116  if (!client.isEnabled()) {
117  context.failure(AuthenticationFlowError.CLIENT_DISABLED, null);
118  return;
119  }
120 
121  // Get client key and validate signature
122  PublicKey clientPublicKey = getSignatureValidationKey(client, context, jws);
123  if (clientPublicKey == null) {
124  // Error response already set to context
125  return;
126  }
127 
128  boolean signatureValid;
129  try {
130  signatureValid = RSAProvider.verify(jws, clientPublicKey);
131  } catch (RuntimeException e) {
132  Throwable cause = e.getCause() != null ? e.getCause() : e;
133  throw new RuntimeException("Signature on JWT token failed validation", cause);
134  }
135  if (!signatureValid) {
136  throw new RuntimeException("Signature on JWT token failed validation");
137  }
138 
139  // Allow both "issuer" or "token-endpoint" as audience
140  String issuerUrl = Urls.realmIssuer(context.getUriInfo().getBaseUri(), realm.getName());
141  String tokenUrl = OIDCLoginProtocolService.tokenUrl(context.getUriInfo().getBaseUriBuilder()).build(realm.getName()).toString();
142  if (!token.hasAudience(issuerUrl) && !token.hasAudience(tokenUrl)) {
143  throw new RuntimeException("Token audience doesn't match domain. Realm issuer is '" + issuerUrl + "' but audience from token is '" + Arrays.asList(token.getAudience()).toString() + "'");
144  }
145 
146  if (!token.isActive()) {
147  throw new RuntimeException("Token is not active");
148  }
149 
150  // KEYCLOAK-2986
151  if (token.getExpiration() == 0 && token.getIssuedAt() + 10 < Time.currentTime()) {
152  throw new RuntimeException("Token is not active");
153  }
154 
155  context.success();
156  } catch (Exception e) {
157  ServicesLogger.LOGGER.errorValidatingAssertion(e);
158  Response challengeResponse = ClientAuthUtil.errorResponse(Response.Status.BAD_REQUEST.getStatusCode(), "unauthorized_client", "Client authentication with signed JWT failed: " + e.getMessage());
159  context.failure(AuthenticationFlowError.INVALID_CLIENT_CREDENTIALS, challengeResponse);
160  }
161  }
PublicKey getSignatureValidationKey(ClientModel client, ClientAuthenticationFlowContext context, JWSInput jws)
Definition: JWTClientAuthenticator.java:163

◆ 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.JWTClientAuthenticator.getAdapterConfiguration ( ClientModel  client)
inline
206  {
207  Map<String, Object> props = new HashMap<>();
208  props.put("client-keystore-file", "REPLACE WITH THE LOCATION OF YOUR KEYSTORE FILE");
209  props.put("client-keystore-type", "jks");
210  props.put("client-keystore-password", "REPLACE WITH THE KEYSTORE PASSWORD");
211  props.put("client-key-password", "REPLACE WITH THE KEY PASSWORD IN KEYSTORE");
212  props.put("client-key-alias", client.getClientId());
213  props.put("token-timeout", 10);
214 
215  Map<String, Object> config = new HashMap<>();
216  config.put("jwt", props);
217  return config;
218  }

◆ getConfigProperties()

List<ProviderConfigProperty> org.keycloak.authentication.authenticators.client.JWTClientAuthenticator.getConfigProperties ( )
inline
195  {
196  return new LinkedList<>();
197  }

◆ getConfigPropertiesPerClient()

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

◆ getDisplayType()

String org.keycloak.authentication.authenticators.client.JWTClientAuthenticator.getDisplayType ( )
inline
175  {
176  return "Signed Jwt";
177  }

◆ getHelpText()

String org.keycloak.authentication.authenticators.client.JWTClientAuthenticator.getHelpText ( )
inline
190  {
191  return "Validates client based on signed JWT issued by client and signed with the Client private key";
192  }

◆ getId()

String org.keycloak.authentication.authenticators.client.JWTClientAuthenticator.getId ( )
inline
221  {
222  return PROVIDER_ID;
223  }
static final String PROVIDER_ID
Definition: JWTClientAuthenticator.java:62

◆ getProtocolAuthenticatorMethods()

Set<String> org.keycloak.authentication.authenticators.client.JWTClientAuthenticator.getProtocolAuthenticatorMethods ( String  loginProtocol)
inline
226  {
227  if (loginProtocol.equals(OIDCLoginProtocol.LOGIN_PROTOCOL)) {
228  Set<String> results = new HashSet<>();
229  results.add(OIDCLoginProtocol.PRIVATE_KEY_JWT);
230  return results;
231  } else {
232  return Collections.emptySet();
233  }
234  }

◆ getReferenceCategory()

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

◆ getRequirementChoices()

AuthenticationExecutionModel.Requirement [] org.keycloak.authentication.authenticators.client.JWTClientAuthenticator.getRequirementChoices ( )
inline
185  {
186  return REQUIREMENT_CHOICES;
187  }
static final AuthenticationExecutionModel.Requirement [] REQUIREMENT_CHOICES
Definition: JWTClientAuthenticator.java:66

◆ getSignatureValidationKey()

PublicKey org.keycloak.authentication.authenticators.client.JWTClientAuthenticator.getSignatureValidationKey ( ClientModel  client,
ClientAuthenticationFlowContext  context,
JWSInput  jws 
)
inlineprotected
163  {
164  PublicKey publicKey = PublicKeyStorageManager.getClientPublicKey(context.getSession(), client, jws);
165  if (publicKey == null) {
166  Response challengeResponse = ClientAuthUtil.errorResponse(Response.Status.BAD_REQUEST.getStatusCode(), "unauthorized_client", "Unable to load public key");
167  context.failure(AuthenticationFlowError.CLIENT_CREDENTIALS_SETUP_REQUIRED, challengeResponse);
168  return null;
169  } else {
170  return publicKey;
171  }
172  }

◆ init()

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

◆ isConfigurable()

boolean org.keycloak.authentication.authenticators.client.JWTClientAuthenticator.isConfigurable ( )
inline
180  {
181  return false;
182  }

◆ 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  }

メンバ詳解

◆ ATTR_PREFIX

final String org.keycloak.authentication.authenticators.client.JWTClientAuthenticator.ATTR_PREFIX = "jwt.credential"
static

◆ CERTIFICATE_ATTR

final String org.keycloak.authentication.authenticators.client.JWTClientAuthenticator.CERTIFICATE_ATTR = "jwt.credential.certificate"
static

◆ PROVIDER_ID

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

◆ REQUIREMENT_CHOICES

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

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