keycloak-service
クラス | 公開メンバ関数 | 限定公開メンバ関数 | 非公開メンバ関数 | 非公開変数類 | 静的非公開変数類 | 全メンバ一覧
org.keycloak.protocol.oidc.endpoints.TokenEndpoint クラス
org.keycloak.protocol.oidc.endpoints.TokenEndpoint 連携図
Collaboration graph

クラス

enum  Action
 

公開メンバ関数

 TokenEndpoint (TokenManager tokenManager, RealmModel realm, EventBuilder event)
 
Response processGrantRequest ()
 
Object introspect ()
 
Response preflight ()
 
Response codeToToken ()
 
Response refreshTokenGrant ()
 
Response resourceOwnerPasswordCredentialsGrant ()
 
Response clientCredentialsGrant ()
 
Response tokenExchange ()
 
Response exchangeToIdentityProvider (UserModel targetUser, UserSessionModel targetUserSession, String requestedIssuer)
 
Response exchangeExternalToken (String issuer, String subjectToken)
 
Response permissionGrant ()
 

限定公開メンバ関数

Response exchangeClientToClient (UserModel targetUser, UserSessionModel targetUserSession)
 
UserModel importUserFromExternalIdentity (BrokeredIdentityContext context)
 

非公開メンバ関数

void checkSsl ()
 
void checkRealm ()
 
void checkClient ()
 
void checkGrantType ()
 
void updateClientSession (AuthenticatedClientSessionModel clientSession)
 
void updateUserSessionFromClientAuth (UserSessionModel userSession)
 
boolean isValidPkceCodeVerifier (String codeVerifier)
 
String generateS256CodeChallenge (String codeVerifier) throws Exception
 

非公開変数類

MultivaluedMap< String, String > formParams
 
ClientModel client
 
Map< String, String > clientAuthAttributes
 
KeycloakSession session
 
HttpRequest request
 
HttpResponse httpResponse
 
HttpHeaders headers
 
ClientConnection clientConnection
 
final TokenManager tokenManager
 
final RealmModel realm
 
final EventBuilder event
 
Action action
 
String grantType
 
Cors cors
 

静的非公開変数類

static final Logger logger = Logger.getLogger(TokenEndpoint.class)
 
static final Pattern VALID_CODE_VERIFIER_PATTERN = Pattern.compile("^[0-9a-zA-Z\\-\\.~_]+$")
 

詳解

著者
Stian Thorgersen

クラス詳解

◆ org::keycloak::protocol::oidc::endpoints::TokenEndpoint::Action

enum org::keycloak::protocol::oidc::endpoints::TokenEndpoint::Action
org.keycloak.protocol.oidc.endpoints.TokenEndpoint.Action 連携図
Collaboration graph
列挙値
AUTHORIZATION_CODE
CLIENT_CREDENTIALS
PASSWORD
PERMISSION
REFRESH_TOKEN
TOKEN_EXCHANGE

構築子と解体子

◆ TokenEndpoint()

org.keycloak.protocol.oidc.endpoints.TokenEndpoint.TokenEndpoint ( TokenManager  tokenManager,
RealmModel  realm,
EventBuilder  event 
)
inline
152  {
153  this.tokenManager = tokenManager;
154  this.realm = realm;
155  this.event = event;
156  }
final TokenManager tokenManager
Definition: TokenEndpoint.java:142
final RealmModel realm
Definition: TokenEndpoint.java:143
final EventBuilder event
Definition: TokenEndpoint.java:144

関数詳解

◆ checkClient()

void org.keycloak.protocol.oidc.endpoints.TokenEndpoint.checkClient ( )
inlineprivate
227  {
228  AuthorizeClientUtil.ClientAuthResult clientAuth = AuthorizeClientUtil.authorizeClient(session, event);
229  client = clientAuth.getClient();
230  clientAuthAttributes = clientAuth.getClientAuthAttributes();
231 
232  cors.allowedOrigins(session.getContext().getUri(), client);
233 
234  if (client.isBearerOnly()) {
235  throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_CLIENT, "Bearer-only not allowed", Response.Status.BAD_REQUEST);
236  }
237 
238 
239  }
Map< String, String > clientAuthAttributes
Definition: TokenEndpoint.java:118
KeycloakSession session
Definition: TokenEndpoint.java:128
Cors cors
Definition: TokenEndpoint.java:150
ClientModel client
Definition: TokenEndpoint.java:117
Set< String > allowedOrigins
Definition: Cors.java:62
final EventBuilder event
Definition: TokenEndpoint.java:144

◆ checkGrantType()

void org.keycloak.protocol.oidc.endpoints.TokenEndpoint.checkGrantType ( )
inlineprivate
241  {
242  if (grantType == null) {
243  throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_REQUEST, "Missing form parameter: " + OIDCLoginProtocol.GRANT_TYPE_PARAM, Response.Status.BAD_REQUEST);
244  }
245 
246  if (grantType.equals(OAuth2Constants.AUTHORIZATION_CODE)) {
247  event.event(EventType.CODE_TO_TOKEN);
248  action = Action.AUTHORIZATION_CODE;
249  } else if (grantType.equals(OAuth2Constants.REFRESH_TOKEN)) {
250  event.event(EventType.REFRESH_TOKEN);
251  action = Action.REFRESH_TOKEN;
252  } else if (grantType.equals(OAuth2Constants.PASSWORD)) {
253  event.event(EventType.LOGIN);
254  action = Action.PASSWORD;
255  } else if (grantType.equals(OAuth2Constants.CLIENT_CREDENTIALS)) {
256  event.event(EventType.CLIENT_LOGIN);
257  action = Action.CLIENT_CREDENTIALS;
258  } else if (grantType.equals(OAuth2Constants.TOKEN_EXCHANGE_GRANT_TYPE)) {
259  event.event(EventType.TOKEN_EXCHANGE);
260  action = Action.TOKEN_EXCHANGE;
261  } else if (grantType.equals(OAuth2Constants.UMA_GRANT_TYPE)) {
262  event.event(EventType.PERMISSION_TOKEN);
263  action = Action.PERMISSION;
264  } else {
265  throw new CorsErrorResponseException(cors, Errors.INVALID_REQUEST, "Invalid " + OIDCLoginProtocol.GRANT_TYPE_PARAM, Response.Status.BAD_REQUEST);
266  }
267 
268  event.detail(Details.GRANT_TYPE, grantType);
269  }
Cors cors
Definition: TokenEndpoint.java:150
String grantType
Definition: TokenEndpoint.java:148
CLIENT_CREDENTIALS
Definition: TokenEndpoint.java:121
Action action
Definition: TokenEndpoint.java:146
AUTHORIZATION_CODE
Definition: TokenEndpoint.java:121
TOKEN_EXCHANGE
Definition: TokenEndpoint.java:121

◆ checkRealm()

void org.keycloak.protocol.oidc.endpoints.TokenEndpoint.checkRealm ( )
inlineprivate
221  {
222  if (!realm.isEnabled()) {
223  throw new CorsErrorResponseException(cors.allowAllOrigins(), "access_denied", "Realm not enabled", Response.Status.FORBIDDEN);
224  }
225  }
Cors cors
Definition: TokenEndpoint.java:150
Cors allowAllOrigins()
Definition: Cors.java:101
final RealmModel realm
Definition: TokenEndpoint.java:143

◆ checkSsl()

void org.keycloak.protocol.oidc.endpoints.TokenEndpoint.checkSsl ( )
inlineprivate
215  {
216  if (!session.getContext().getUri().getBaseUri().getScheme().equals("https") && realm.getSslRequired().isRequired(clientConnection)) {
217  throw new CorsErrorResponseException(cors.allowAllOrigins(), OAuthErrorException.INVALID_REQUEST, "HTTPS required", Response.Status.FORBIDDEN);
218  }
219  }
KeycloakSession session
Definition: TokenEndpoint.java:128
Cors cors
Definition: TokenEndpoint.java:150
Cors allowAllOrigins()
Definition: Cors.java:101
ClientConnection clientConnection
Definition: TokenEndpoint.java:140
final RealmModel realm
Definition: TokenEndpoint.java:143

◆ clientCredentialsGrant()

Response org.keycloak.protocol.oidc.endpoints.TokenEndpoint.clientCredentialsGrant ( )
inline
585  {
586  if (client.isBearerOnly()) {
587  event.error(Errors.INVALID_CLIENT);
588  throw new CorsErrorResponseException(cors, OAuthErrorException.UNAUTHORIZED_CLIENT, "Bearer-only client not allowed to retrieve service account", Response.Status.UNAUTHORIZED);
589  }
590  if (client.isPublicClient()) {
591  event.error(Errors.INVALID_CLIENT);
592  throw new CorsErrorResponseException(cors, OAuthErrorException.UNAUTHORIZED_CLIENT, "Public client not allowed to retrieve service account", Response.Status.UNAUTHORIZED);
593  }
594  if (!client.isServiceAccountsEnabled()) {
595  event.error(Errors.INVALID_CLIENT);
596  throw new CorsErrorResponseException(cors, OAuthErrorException.UNAUTHORIZED_CLIENT, "Client not enabled to retrieve service account", Response.Status.UNAUTHORIZED);
597  }
598 
599  UserModel clientUser = session.users().getServiceAccount(client);
600 
601  if (clientUser == null || client.getProtocolMapperByName(OIDCLoginProtocol.LOGIN_PROTOCOL, ServiceAccountConstants.CLIENT_ID_PROTOCOL_MAPPER) == null) {
602  // May need to handle bootstrap here as well
603  logger.debugf("Service account user for client '%s' not found or default protocol mapper for service account not found. Creating now", client.getClientId());
604  new ClientManager(new RealmManager(session)).enableServiceAccount(client);
605  clientUser = session.users().getServiceAccount(client);
606  }
607 
608  String clientUsername = clientUser.getUsername();
609  event.detail(Details.USERNAME, clientUsername);
610  event.user(clientUser);
611 
612  if (!clientUser.isEnabled()) {
613  event.error(Errors.USER_DISABLED);
614  throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_REQUEST, "User '" + clientUsername + "' disabled", Response.Status.UNAUTHORIZED);
615  }
616 
617  String scope = formParams.getFirst(OAuth2Constants.SCOPE);
618 
619  RootAuthenticationSessionModel rootAuthSession = new AuthenticationSessionManager(session).createAuthenticationSession(realm, false);
620  AuthenticationSessionModel authSession = rootAuthSession.createAuthenticationSession(client);
621 
622  authSession.setAuthenticatedUser(clientUser);
623  authSession.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
624  authSession.setClientNote(OIDCLoginProtocol.ISSUER, Urls.realmIssuer(session.getContext().getUri().getBaseUri(), realm.getName()));
625  authSession.setClientNote(OIDCLoginProtocol.SCOPE_PARAM, scope);
626 
627  UserSessionModel userSession = session.sessions().createUserSession(authSession.getParentSession().getId(), realm, clientUser, clientUsername,
628  clientConnection.getRemoteAddr(), ServiceAccountConstants.CLIENT_AUTH, false, null, null);
629  event.session(userSession);
630 
631  AuthenticationManager.setClientScopesInSession(authSession);
632  ClientSessionContext clientSessionCtx = TokenManager.attachAuthenticationSession(session, userSession, authSession);
633 
634  // Notes about client details
635  userSession.setNote(ServiceAccountConstants.CLIENT_ID, client.getClientId());
636  userSession.setNote(ServiceAccountConstants.CLIENT_HOST, clientConnection.getRemoteHost());
637  userSession.setNote(ServiceAccountConstants.CLIENT_ADDRESS, clientConnection.getRemoteAddr());
638 
639  updateUserSessionFromClientAuth(userSession);
640 
641  TokenManager.AccessTokenResponseBuilder responseBuilder = tokenManager.responseBuilder(realm, client, event, session, userSession, clientSessionCtx)
644 
645  String scopeParam = clientSessionCtx.getClientSession().getNote(OAuth2Constants.SCOPE);
646  if (TokenUtil.isOIDCRequest(scopeParam)) {
647  responseBuilder.generateIDToken();
648  }
649 
650  AccessTokenResponse res = responseBuilder.build();
651 
652  event.success();
653 
654  return cors.builder(Response.ok(res, MediaType.APPLICATION_JSON_TYPE)).build();
655  }
AccessTokenResponse build()
Definition: TokenManager.java:869
KeycloakSession session
Definition: TokenEndpoint.java:128
Cors cors
Definition: TokenEndpoint.java:150
AccessTokenResponseBuilder generateRefreshToken()
Definition: TokenManager.java:799
AccessTokenResponseBuilder generateIDToken()
Definition: TokenManager.java:831
ClientModel client
Definition: TokenEndpoint.java:117
static final Logger logger
Definition: TokenEndpoint.java:115
AccessTokenResponseBuilder responseBuilder(RealmModel realm, ClientModel client, EventBuilder event, KeycloakSession session, UserSessionModel userSession, ClientSessionContext clientSessionCtx)
Definition: TokenManager.java:740
MultivaluedMap< String, String > formParams
Definition: TokenEndpoint.java:116
ResponseBuilder builder
Definition: Cors.java:61
ClientConnection clientConnection
Definition: TokenEndpoint.java:140
final TokenManager tokenManager
Definition: TokenEndpoint.java:142
final RealmModel realm
Definition: TokenEndpoint.java:143
void updateUserSessionFromClientAuth(UserSessionModel userSession)
Definition: TokenEndpoint.java:510
AccessTokenResponseBuilder generateAccessToken()
Definition: TokenManager.java:793
final EventBuilder event
Definition: TokenEndpoint.java:144

◆ codeToToken()

Response org.keycloak.protocol.oidc.endpoints.TokenEndpoint.codeToToken ( )
inline
271  {
272  String code = formParams.getFirst(OAuth2Constants.CODE);
273  if (code == null) {
274  event.error(Errors.INVALID_CODE);
275  throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_REQUEST, "Missing parameter: " + OAuth2Constants.CODE, Response.Status.BAD_REQUEST);
276  }
277 
278  ClientSessionCode.ParseResult<AuthenticatedClientSessionModel> parseResult = ClientSessionCode.parseResult(code, null, session, realm, client, event, AuthenticatedClientSessionModel.class);
279  if (parseResult.isAuthSessionNotFound() || parseResult.isIllegalHash()) {
280  AuthenticatedClientSessionModel clientSession = parseResult.getClientSession();
281 
282  // Attempt to use same code twice should invalidate existing clientSession
283  if (clientSession != null) {
284  clientSession.detachFromUserSession();
285  }
286 
287  event.error(Errors.INVALID_CODE);
288 
289  throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_GRANT, "Code not valid", Response.Status.BAD_REQUEST);
290  }
291 
292  AuthenticatedClientSessionModel clientSession = parseResult.getClientSession();
293 
294  if (parseResult.isExpiredToken()) {
295  event.error(Errors.EXPIRED_CODE);
296  throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_GRANT, "Code is expired", Response.Status.BAD_REQUEST);
297  }
298 
299  UserSessionModel userSession = clientSession.getUserSession();
300 
301  if (userSession == null) {
302  event.error(Errors.USER_SESSION_NOT_FOUND);
303  throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_GRANT, "User session not found", Response.Status.BAD_REQUEST);
304  }
305 
306 
307  UserModel user = userSession.getUser();
308  if (user == null) {
309  event.error(Errors.USER_NOT_FOUND);
310  throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_GRANT, "User not found", Response.Status.BAD_REQUEST);
311  }
312 
313  event.user(userSession.getUser());
314 
315  if (!user.isEnabled()) {
316  event.error(Errors.USER_DISABLED);
317  throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_GRANT, "User disabled", Response.Status.BAD_REQUEST);
318  }
319 
320  String redirectUri = clientSession.getNote(OIDCLoginProtocol.REDIRECT_URI_PARAM);
321  String redirectUriParam = formParams.getFirst(OAuth2Constants.REDIRECT_URI);
322 
323  // KEYCLOAK-4478 Backwards compatibility with the adapters earlier than KC 3.4.2
324  if (redirectUriParam != null && redirectUriParam.contains("session_state=") && !redirectUri.contains("session_state=")) {
325  redirectUriParam = KeycloakUriBuilder.fromUri(redirectUriParam)
326  .replaceQueryParam(OAuth2Constants.SESSION_STATE, null)
327  .build().toString();
328  }
329 
330  if (redirectUri != null && !redirectUri.equals(redirectUriParam)) {
331  event.error(Errors.INVALID_CODE);
332  throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_GRANT, "Incorrect redirect_uri", Response.Status.BAD_REQUEST);
333  }
334 
335  if (!client.getClientId().equals(clientSession.getClient().getClientId())) {
336  event.error(Errors.INVALID_CODE);
337  throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_GRANT, "Auth error", Response.Status.BAD_REQUEST);
338  }
339 
340  if (!client.isStandardFlowEnabled()) {
341  event.error(Errors.NOT_ALLOWED);
342  throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_GRANT, "Client not allowed to exchange code", Response.Status.BAD_REQUEST);
343  }
344 
345  if (!AuthenticationManager.isSessionValid(realm, userSession)) {
346  event.error(Errors.USER_SESSION_NOT_FOUND);
347  throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_GRANT, "Session not active", Response.Status.BAD_REQUEST);
348  }
349 
350  // https://tools.ietf.org/html/rfc7636#section-4.6
351  String codeVerifier = formParams.getFirst(OAuth2Constants.CODE_VERIFIER);
352  String codeChallenge = clientSession.getNote(OIDCLoginProtocol.CODE_CHALLENGE_PARAM);
353  String codeChallengeMethod = clientSession.getNote(OIDCLoginProtocol.CODE_CHALLENGE_METHOD_PARAM);
354  String authUserId = user.getId();
355  String authUsername = user.getUsername();
356  if (authUserId == null) {
357  authUserId = "unknown";
358  }
359  if (authUsername == null) {
360  authUsername = "unknown";
361  }
362  if (codeChallenge != null && codeVerifier == null) {
363  logger.warnf("PKCE code verifier not specified, authUserId = %s, authUsername = %s", authUserId, authUsername);
364  event.error(Errors.CODE_VERIFIER_MISSING);
365  throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_GRANT, "PKCE code verifier not specified", Response.Status.BAD_REQUEST);
366  }
367 
368  if (codeChallenge != null) {
369  // based on whether code_challenge has been stored at corresponding authorization code request previously
370  // decide whether this client(RP) supports PKCE
371  if (!isValidPkceCodeVerifier(codeVerifier)) {
372  logger.infof("PKCE invalid code verifier");
373  event.error(Errors.INVALID_CODE_VERIFIER);
374  throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_GRANT, "PKCE invalid code verifier", Response.Status.BAD_REQUEST);
375  }
376 
377  logger.debugf("PKCE supporting Client, codeVerifier = %s", codeVerifier);
378  String codeVerifierEncoded = codeVerifier;
379  try {
380  // https://tools.ietf.org/html/rfc7636#section-4.2
381  // plain or S256
382  if (codeChallengeMethod != null && codeChallengeMethod.equals(OAuth2Constants.PKCE_METHOD_S256)) {
383  logger.debugf("PKCE codeChallengeMethod = %s", codeChallengeMethod);
384  codeVerifierEncoded = generateS256CodeChallenge(codeVerifier);
385  } else {
386  logger.debug("PKCE codeChallengeMethod is plain");
387  codeVerifierEncoded = codeVerifier;
388  }
389  } catch (Exception nae) {
390  logger.infof("PKCE code verification failed, not supported algorithm specified");
391  event.error(Errors.PKCE_VERIFICATION_FAILED);
392  throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_GRANT, "PKCE code verification failed, not supported algorithm specified", Response.Status.BAD_REQUEST);
393  }
394  if (!codeChallenge.equals(codeVerifierEncoded)) {
395  logger.warnf("PKCE verification failed. authUserId = %s, authUsername = %s", authUserId, authUsername);
396  event.error(Errors.PKCE_VERIFICATION_FAILED);
397  throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_GRANT, "PKCE verification failed", Response.Status.BAD_REQUEST);
398  } else {
399  logger.debugf("PKCE verification success. codeVerifierEncoded = %s, codeChallenge = %s", codeVerifierEncoded, codeChallenge);
400  }
401  }
402 
403 
404  updateClientSession(clientSession);
405  updateUserSessionFromClientAuth(userSession);
406 
407  // Compute client scopes again from scope parameter. Check if user still has them granted
408  // (but in code-to-token request, it could just theoretically happen that they are not available)
409  String scopeParam = clientSession.getNote(OAuth2Constants.SCOPE);
410  Set<ClientScopeModel> clientScopes = TokenManager.getRequestedClientScopes(scopeParam, client);
411  if (!TokenManager.verifyConsentStillAvailable(session, user, client, clientScopes)) {
412  event.error(Errors.NOT_ALLOWED);
413  throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_SCOPE, "Client no longer has requested consent from user", Response.Status.BAD_REQUEST);
414  }
415 
416  ClientSessionContext clientSessionCtx = DefaultClientSessionContext.fromClientSessionAndClientScopes(clientSession, clientScopes);
417 
418  AccessToken token = tokenManager.createClientAccessToken(session, realm, client, user, userSession, clientSessionCtx);
419 
420  TokenManager.AccessTokenResponseBuilder responseBuilder = tokenManager.responseBuilder(realm, client, event, session, userSession, clientSessionCtx)
421  .accessToken(token)
422  .generateRefreshToken();
423 
424  // KEYCLOAK-6771 Certificate Bound Token
425  // https://tools.ietf.org/html/draft-ietf-oauth-mtls-08#section-3
426  if (OIDCAdvancedConfigWrapper.fromClientModel(client).isUseMtlsHokToken()) {
427  AccessToken.CertConf certConf = MtlsHoKTokenUtil.bindTokenWithClientCertificate(request, session);
428  if (certConf != null) {
429  responseBuilder.getAccessToken().setCertConf(certConf);
430  responseBuilder.getRefreshToken().setCertConf(certConf);
431  } else {
432  event.error(Errors.INVALID_REQUEST);
433  throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_REQUEST, "Client Certification missing for MTLS HoK Token Binding", Response.Status.BAD_REQUEST);
434  }
435  }
436 
437  if (TokenUtil.isOIDCRequest(scopeParam)) {
438  responseBuilder.generateIDToken();
439  }
440 
441  AccessTokenResponse res = responseBuilder.build();
442 
443  event.success();
444 
445  return cors.builder(Response.ok(res).type(MediaType.APPLICATION_JSON_TYPE)).build();
446  }
void updateClientSession(AuthenticatedClientSessionModel clientSession)
Definition: TokenEndpoint.java:484
AccessToken accessToken
Definition: TokenManager.java:753
KeycloakSession session
Definition: TokenEndpoint.java:128
Cors cors
Definition: TokenEndpoint.java:150
String generateS256CodeChallenge(String codeVerifier)
Definition: TokenEndpoint.java:1158
ClientModel client
Definition: TokenEndpoint.java:117
boolean isValidPkceCodeVerifier(String codeVerifier)
Definition: TokenEndpoint.java:1144
static final Logger logger
Definition: TokenEndpoint.java:115
AccessTokenResponseBuilder responseBuilder(RealmModel realm, ClientModel client, EventBuilder event, KeycloakSession session, UserSessionModel userSession, ClientSessionContext clientSessionCtx)
Definition: TokenManager.java:740
HttpRequest request
Definition: TokenEndpoint.java:131
AccessToken createClientAccessToken(KeycloakSession session, RealmModel realm, ClientModel client, UserModel user, UserSessionModel userSession, ClientSessionContext clientSessionCtx)
Definition: TokenManager.java:415
MultivaluedMap< String, String > formParams
Definition: TokenEndpoint.java:116
ResponseBuilder builder
Definition: Cors.java:61
final TokenManager tokenManager
Definition: TokenEndpoint.java:142
final RealmModel realm
Definition: TokenEndpoint.java:143
void updateUserSessionFromClientAuth(UserSessionModel userSession)
Definition: TokenEndpoint.java:510
final EventBuilder event
Definition: TokenEndpoint.java:144

◆ exchangeClientToClient()

Response org.keycloak.protocol.oidc.endpoints.TokenEndpoint.exchangeClientToClient ( UserModel  targetUser,
UserSessionModel  targetUserSession 
)
inlineprotected
801  {
802  String requestedTokenType = formParams.getFirst(OAuth2Constants.REQUESTED_TOKEN_TYPE);
803  if (requestedTokenType == null) {
804  requestedTokenType = OAuth2Constants.REFRESH_TOKEN_TYPE;
805  } else if (!requestedTokenType.equals(OAuth2Constants.ACCESS_TOKEN_TYPE) && !requestedTokenType.equals(OAuth2Constants.REFRESH_TOKEN_TYPE)) {
806  event.detail(Details.REASON, "requested_token_type unsupported");
807  event.error(Errors.INVALID_REQUEST);
808  throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_REQUEST, "requested_token_type unsupported", Response.Status.BAD_REQUEST);
809 
810  }
811  ClientModel targetClient = client;
812  String audience = formParams.getFirst(OAuth2Constants.AUDIENCE);
813  if (audience != null) {
814  targetClient = realm.getClientByClientId(audience);
815  if (targetClient == null) {
816  event.detail(Details.REASON, "audience not found");
817  event.error(Errors.CLIENT_NOT_FOUND);
818  throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_CLIENT, "Audience not found", Response.Status.BAD_REQUEST);
819 
820  }
821  }
822 
823 
824  if (targetClient.isConsentRequired()) {
825  event.detail(Details.REASON, "audience requires consent");
826  event.error(Errors.CONSENT_DENIED);
827  throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_CLIENT, "Client requires user consent", Response.Status.BAD_REQUEST);
828  }
829 
830  if (!targetClient.equals(client) && !AdminPermissions.management(session, realm).clients().canExchangeTo(client, targetClient)) {
831  event.detail(Details.REASON, "client not allowed to exchange to audience");
832  event.error(Errors.NOT_ALLOWED);
833  throw new CorsErrorResponseException(cors, OAuthErrorException.ACCESS_DENIED, "Client not allowed to exchange", Response.Status.FORBIDDEN);
834  }
835 
836  String scope = formParams.getFirst(OAuth2Constants.SCOPE);
837 
838  RootAuthenticationSessionModel rootAuthSession = new AuthenticationSessionManager(session).createAuthenticationSession(realm, false);
839  AuthenticationSessionModel authSession = rootAuthSession.createAuthenticationSession(targetClient);
840 
841  authSession.setAuthenticatedUser(targetUser);
842  authSession.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
843  authSession.setClientNote(OIDCLoginProtocol.ISSUER, Urls.realmIssuer(session.getContext().getUri().getBaseUri(), realm.getName()));
844  authSession.setClientNote(OIDCLoginProtocol.SCOPE_PARAM, scope);
845 
846  event.session(targetUserSession);
847 
848  AuthenticationManager.setClientScopesInSession(authSession);
849  ClientSessionContext clientSessionCtx = TokenManager.attachAuthenticationSession(this.session, targetUserSession, authSession);
850 
851  updateUserSessionFromClientAuth(targetUserSession);
852 
853  TokenManager.AccessTokenResponseBuilder responseBuilder = tokenManager.responseBuilder(realm, targetClient, event, this.session, targetUserSession, clientSessionCtx)
855  responseBuilder.getAccessToken().issuedFor(client.getClientId());
856 
857  if (requestedTokenType.equals(OAuth2Constants.REFRESH_TOKEN_TYPE)) {
858  responseBuilder.generateRefreshToken();
859  responseBuilder.getRefreshToken().issuedFor(client.getClientId());
860  }
861 
862  String scopeParam = clientSessionCtx.getClientSession().getNote(OAuth2Constants.SCOPE);
863  if (TokenUtil.isOIDCRequest(scopeParam)) {
864  responseBuilder.generateIDToken();
865  }
866 
867  AccessTokenResponse res = responseBuilder.build();
868  event.detail(Details.AUDIENCE, targetClient.getClientId());
869 
870  event.success();
871 
872  return cors.builder(Response.ok(res, MediaType.APPLICATION_JSON_TYPE)).build();
873  }
KeycloakSession session
Definition: TokenEndpoint.java:128
Cors cors
Definition: TokenEndpoint.java:150
ClientModel client
Definition: TokenEndpoint.java:117
AccessToken getAccessToken()
Definition: TokenManager.java:772
AccessTokenResponseBuilder responseBuilder(RealmModel realm, ClientModel client, EventBuilder event, KeycloakSession session, UserSessionModel userSession, ClientSessionContext clientSessionCtx)
Definition: TokenManager.java:740
MultivaluedMap< String, String > formParams
Definition: TokenEndpoint.java:116
ResponseBuilder builder
Definition: Cors.java:61
final TokenManager tokenManager
Definition: TokenEndpoint.java:142
final RealmModel realm
Definition: TokenEndpoint.java:143
void updateUserSessionFromClientAuth(UserSessionModel userSession)
Definition: TokenEndpoint.java:510
AccessTokenResponseBuilder generateAccessToken()
Definition: TokenManager.java:793
final EventBuilder event
Definition: TokenEndpoint.java:144

◆ exchangeExternalToken()

Response org.keycloak.protocol.oidc.endpoints.TokenEndpoint.exchangeExternalToken ( String  issuer,
String  subjectToken 
)
inline
875  {
876  ExchangeExternalToken externalIdp = null;
877  IdentityProviderModel externalIdpModel = null;
878 
879  for (IdentityProviderModel idpModel : realm.getIdentityProviders()) {
880  IdentityProviderFactory factory = IdentityBrokerService.getIdentityProviderFactory(session, idpModel);
881  IdentityProvider idp = factory.create(session, idpModel);
882  if (idp instanceof ExchangeExternalToken) {
883  ExchangeExternalToken external = (ExchangeExternalToken) idp;
884  if (idpModel.getAlias().equals(issuer) || external.isIssuer(issuer, formParams)) {
885  externalIdp = external;
886  externalIdpModel = idpModel;
887  break;
888  }
889  }
890  }
891 
892 
893  if (externalIdp == null) {
894  event.error(Errors.INVALID_ISSUER);
895  throw new CorsErrorResponseException(cors, Errors.INVALID_ISSUER, "Invalid " + OAuth2Constants.SUBJECT_ISSUER + " parameter", Response.Status.BAD_REQUEST);
896  }
897  if (!AdminPermissions.management(session, realm).idps().canExchangeTo(client, externalIdpModel)) {
898  event.detail(Details.REASON, "client not allowed to exchange subject_issuer");
899  event.error(Errors.NOT_ALLOWED);
900  throw new CorsErrorResponseException(cors, OAuthErrorException.ACCESS_DENIED, "Client not allowed to exchange", Response.Status.FORBIDDEN);
901  }
902  BrokeredIdentityContext context = externalIdp.exchangeExternal(event, formParams);
903  if (context == null) {
904  event.error(Errors.INVALID_ISSUER);
905  throw new CorsErrorResponseException(cors, Errors.INVALID_ISSUER, "Invalid " + OAuth2Constants.SUBJECT_ISSUER + " parameter", Response.Status.BAD_REQUEST);
906  }
907 
908  UserModel user = importUserFromExternalIdentity(context);
909 
910  UserSessionModel userSession = session.sessions().createUserSession(realm, user, user.getUsername(), clientConnection.getRemoteAddr(), "external-exchange", false, null, null);
911  externalIdp.exchangeExternalComplete(userSession, context, formParams);
912 
913  // this must exist so that we can obtain access token from user session if idp's store tokens is off
914  userSession.setNote(IdentityProvider.EXTERNAL_IDENTITY_PROVIDER, externalIdpModel.getAlias());
915  userSession.setNote(IdentityProvider.FEDERATED_ACCESS_TOKEN, subjectToken);
916 
917  return exchangeClientToClient(user, userSession);
918 
919 
920  }
KeycloakSession session
Definition: TokenEndpoint.java:128
Cors cors
Definition: TokenEndpoint.java:150
Response exchangeClientToClient(UserModel targetUser, UserSessionModel targetUserSession)
Definition: TokenEndpoint.java:801
UserModel importUserFromExternalIdentity(BrokeredIdentityContext context)
Definition: TokenEndpoint.java:922
ClientModel client
Definition: TokenEndpoint.java:117
MultivaluedMap< String, String > formParams
Definition: TokenEndpoint.java:116
ClientConnection clientConnection
Definition: TokenEndpoint.java:140
final RealmModel realm
Definition: TokenEndpoint.java:143
final EventBuilder event
Definition: TokenEndpoint.java:144

◆ exchangeToIdentityProvider()

Response org.keycloak.protocol.oidc.endpoints.TokenEndpoint.exchangeToIdentityProvider ( UserModel  targetUser,
UserSessionModel  targetUserSession,
String  requestedIssuer 
)
inline
776  {
777  event.detail(Details.REQUESTED_ISSUER, requestedIssuer);
778  IdentityProviderModel providerModel = realm.getIdentityProviderByAlias(requestedIssuer);
779  if (providerModel == null) {
780  event.detail(Details.REASON, "unknown requested_issuer");
781  event.error(Errors.UNKNOWN_IDENTITY_PROVIDER);
782  throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_REQUEST, "Invalid issuer", Response.Status.BAD_REQUEST);
783  }
784 
785  IdentityProvider provider = IdentityBrokerService.getIdentityProvider(session, realm, requestedIssuer);
786  if (!(provider instanceof ExchangeTokenToIdentityProviderToken)) {
787  event.detail(Details.REASON, "exchange unsupported by requested_issuer");
788  event.error(Errors.UNKNOWN_IDENTITY_PROVIDER);
789  throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_REQUEST, "Issuer does not support token exchange", Response.Status.BAD_REQUEST);
790  }
791  if (!AdminPermissions.management(session, realm).idps().canExchangeTo(client, providerModel)) {
792  event.detail(Details.REASON, "client not allowed to exchange for requested_issuer");
793  event.error(Errors.NOT_ALLOWED);
794  throw new CorsErrorResponseException(cors, OAuthErrorException.ACCESS_DENIED, "Client not allowed to exchange", Response.Status.FORBIDDEN);
795  }
796  Response response = ((ExchangeTokenToIdentityProviderToken)provider).exchangeFromToken(session.getContext().getUri(), event, client, targetUserSession, targetUser, formParams);
797  return cors.builder(Response.fromResponse(response)).build();
798 
799  }
KeycloakSession session
Definition: TokenEndpoint.java:128
Cors cors
Definition: TokenEndpoint.java:150
ClientModel client
Definition: TokenEndpoint.java:117
MultivaluedMap< String, String > formParams
Definition: TokenEndpoint.java:116
ResponseBuilder builder
Definition: Cors.java:61
final RealmModel realm
Definition: TokenEndpoint.java:143
final EventBuilder event
Definition: TokenEndpoint.java:144

◆ generateS256CodeChallenge()

String org.keycloak.protocol.oidc.endpoints.TokenEndpoint.generateS256CodeChallenge ( String  codeVerifier) throws Exception
inlineprivate
1158  {
1159  MessageDigest md = MessageDigest.getInstance("SHA-256");
1160  md.update(codeVerifier.getBytes("ISO_8859_1"));
1161  byte[] digestBytes = md.digest();
1162  String codeVerifierEncoded = Base64Url.encode(digestBytes);
1163  return codeVerifierEncoded;
1164  }

◆ importUserFromExternalIdentity()

UserModel org.keycloak.protocol.oidc.endpoints.TokenEndpoint.importUserFromExternalIdentity ( BrokeredIdentityContext  context)
inlineprotected
922  {
923  IdentityProviderModel identityProviderConfig = context.getIdpConfig();
924 
925  String providerId = identityProviderConfig.getAlias();
926 
927  // do we need this?
928  //AuthenticationSessionModel authenticationSession = clientCode.getClientSession();
929  //context.setAuthenticationSession(authenticationSession);
930  //session.getContext().setClient(authenticationSession.getClient());
931 
932  context.getIdp().preprocessFederatedIdentity(session, realm, context);
933  Set<IdentityProviderMapperModel> mappers = realm.getIdentityProviderMappersByAlias(context.getIdpConfig().getAlias());
934  if (mappers != null) {
935  KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory();
936  for (IdentityProviderMapperModel mapper : mappers) {
937  IdentityProviderMapper target = (IdentityProviderMapper)sessionFactory.getProviderFactory(IdentityProviderMapper.class, mapper.getIdentityProviderMapper());
938  target.preprocessFederatedIdentity(session, realm, mapper, context);
939  }
940  }
941 
942  FederatedIdentityModel federatedIdentityModel = new FederatedIdentityModel(providerId, context.getId(),
943  context.getUsername(), context.getToken());
944 
945  UserModel user = this.session.users().getUserByFederatedIdentity(federatedIdentityModel, realm);
946 
947  if (user == null) {
948 
949  logger.debugf("Federated user not found for provider '%s' and broker username '%s'.", providerId, context.getUsername());
950 
951  String username = context.getModelUsername();
952  if (username == null) {
953  if (this.realm.isRegistrationEmailAsUsername() && !Validation.isBlank(context.getEmail())) {
954  username = context.getEmail();
955  } else if (context.getUsername() == null) {
956  username = context.getIdpConfig().getAlias() + "." + context.getId();
957  } else {
958  username = context.getUsername();
959  }
960  }
961  username = username.trim();
962  context.setModelUsername(username);
963  if (context.getEmail() != null && !realm.isDuplicateEmailsAllowed()) {
964  UserModel existingUser = session.users().getUserByEmail(context.getEmail(), realm);
965  if (existingUser != null) {
966  event.error(Errors.FEDERATED_IDENTITY_EXISTS);
967  throw new CorsErrorResponseException(cors, Errors.INVALID_TOKEN, "User already exists", Response.Status.BAD_REQUEST);
968  }
969  }
970 
971  UserModel existingUser = session.users().getUserByUsername(username, realm);
972  if (existingUser != null) {
973  event.error(Errors.FEDERATED_IDENTITY_EXISTS);
974  throw new CorsErrorResponseException(cors, Errors.INVALID_TOKEN, "User already exists", Response.Status.BAD_REQUEST);
975  }
976 
977 
978  user = session.users().addUser(realm, username);
979  user.setEnabled(true);
980  user.setEmail(context.getEmail());
981  user.setFirstName(context.getFirstName());
982  user.setLastName(context.getLastName());
983 
984 
985  federatedIdentityModel = new FederatedIdentityModel(context.getIdpConfig().getAlias(), context.getId(),
986  context.getUsername(), context.getToken());
987  session.users().addFederatedIdentity(realm, user, federatedIdentityModel);
988 
989  context.getIdp().importNewUser(session, realm, user, context);
990  if (mappers != null) {
991  KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory();
992  for (IdentityProviderMapperModel mapper : mappers) {
993  IdentityProviderMapper target = (IdentityProviderMapper)sessionFactory.getProviderFactory(IdentityProviderMapper.class, mapper.getIdentityProviderMapper());
994  target.importNewUser(session, realm, user, mapper, context);
995  }
996  }
997 
998  if (context.getIdpConfig().isTrustEmail() && !Validation.isBlank(user.getEmail())) {
999  logger.debugf("Email verified automatically after registration of user '%s' through Identity provider '%s' ", user.getUsername(), context.getIdpConfig().getAlias());
1000  user.setEmailVerified(true);
1001  }
1002  } else {
1003  if (!user.isEnabled()) {
1004  event.error(Errors.USER_DISABLED);
1005  throw new CorsErrorResponseException(cors, Errors.INVALID_TOKEN, "Invalid Token", Response.Status.BAD_REQUEST);
1006  }
1007  if (realm.isBruteForceProtected()) {
1008  if (session.getProvider(BruteForceProtector.class).isTemporarilyDisabled(session, realm, user)) {
1009  event.error(Errors.USER_TEMPORARILY_DISABLED);
1010  throw new CorsErrorResponseException(cors, Errors.INVALID_TOKEN, "Invalid Token", Response.Status.BAD_REQUEST);
1011  }
1012  }
1013 
1014  context.getIdp().updateBrokeredUser(session, realm, user, context);
1015  if (mappers != null) {
1016  KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory();
1017  for (IdentityProviderMapperModel mapper : mappers) {
1018  IdentityProviderMapper target = (IdentityProviderMapper)sessionFactory.getProviderFactory(IdentityProviderMapper.class, mapper.getIdentityProviderMapper());
1019  target.updateBrokeredUser(session, realm, user, mapper, context);
1020  }
1021  }
1022  }
1023  return user;
1024  }
KeycloakSession session
Definition: TokenEndpoint.java:128
Cors cors
Definition: TokenEndpoint.java:150
static final Logger logger
Definition: TokenEndpoint.java:115
final RealmModel realm
Definition: TokenEndpoint.java:143

◆ introspect()

Object org.keycloak.protocol.oidc.endpoints.TokenEndpoint.introspect ( )
inline
199  {
200  TokenIntrospectionEndpoint tokenIntrospectionEndpoint = new TokenIntrospectionEndpoint(this.realm, this.event);
201 
202  ResteasyProviderFactory.getInstance().injectProperties(tokenIntrospectionEndpoint);
203 
204  return tokenIntrospectionEndpoint;
205  }
final RealmModel realm
Definition: TokenEndpoint.java:143
final EventBuilder event
Definition: TokenEndpoint.java:144

◆ isValidPkceCodeVerifier()

boolean org.keycloak.protocol.oidc.endpoints.TokenEndpoint.isValidPkceCodeVerifier ( String  codeVerifier)
inlineprivate
1144  {
1145  if (codeVerifier.length() < OIDCLoginProtocol.PKCE_CODE_VERIFIER_MIN_LENGTH) {
1146  logger.infof(" Error: PKCE codeVerifier length under lower limit , codeVerifier = %s", codeVerifier);
1147  return false;
1148  }
1149  if (codeVerifier.length() > OIDCLoginProtocol.PKCE_CODE_VERIFIER_MAX_LENGTH) {
1150  logger.infof(" Error: PKCE codeVerifier length over upper limit , codeVerifier = %s", codeVerifier);
1151  return false;
1152  }
1153  Matcher m = VALID_CODE_VERIFIER_PATTERN.matcher(codeVerifier);
1154  return m.matches() ? true : false;
1155  }
static final Logger logger
Definition: TokenEndpoint.java:115
static final Pattern VALID_CODE_VERIFIER_PATTERN
Definition: TokenEndpoint.java:125

◆ permissionGrant()

Response org.keycloak.protocol.oidc.endpoints.TokenEndpoint.permissionGrant ( )
inline
1026  {
1027  event.detail(Details.AUTH_METHOD, "oauth_credentials");
1028 
1029  String accessTokenString = null;
1030  String authorizationHeader = headers.getRequestHeaders().getFirst(HttpHeaders.AUTHORIZATION);
1031 
1032  if (authorizationHeader != null && authorizationHeader.toLowerCase().startsWith("bearer")) {
1033  accessTokenString = new AppAuthManager().extractAuthorizationHeaderToken(headers);
1034  }
1035 
1036  // we allow public clients to authenticate using a bearer token, where the token should be a valid access token.
1037  // public clients don't have secret and should be able to obtain a RPT by providing an access token previously issued by the server
1038  if (accessTokenString != null) {
1039  AccessToken accessToken = Tokens.getAccessToken(session);
1040 
1041  if (accessToken == null) {
1042  throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_GRANT, "Invalid bearer token", Status.UNAUTHORIZED);
1043  }
1044 
1045  ClientModel client = realm.getClientByClientId(accessToken.getIssuedFor());
1046 
1047  session.getContext().setClient(client);
1048 
1049  cors.allowedOrigins(session.getContext().getUri(), client);
1050  }
1051 
1052  String claimToken = null;
1053 
1054  // claim_token is optional, if provided we just grab it from the request
1055  if (formParams.containsKey("claim_token")) {
1056  claimToken = formParams.get("claim_token").get(0);
1057  }
1058 
1059  String claimTokenFormat = formParams.getFirst("claim_token_format");
1060 
1061  if (claimToken != null && claimTokenFormat == null) {
1062  claimTokenFormat = AuthorizationTokenService.CLAIM_TOKEN_FORMAT_ID_TOKEN;
1063  }
1064 
1065  if (accessTokenString == null) {
1066  // in case no bearer token is provided, we force client authentication
1067  checkClient();
1068 
1069  // if a claim token is provided, we check if the format is a OpenID Connect IDToken and assume the token represents the identity asking for permissions
1070  if (AuthorizationTokenService.CLAIM_TOKEN_FORMAT_ID_TOKEN.equalsIgnoreCase(claimTokenFormat)) {
1071  accessTokenString = claimToken;
1072  } else {
1073  // Clients need to authenticate in order to obtain a RPT from the server.
1074  // In order to support cases where the client is obtaining permissions on its on behalf, we issue a temporary access token
1075  accessTokenString = AccessTokenResponse.class.cast(clientCredentialsGrant().getEntity()).getToken();
1076  }
1077  }
1078 
1079  AuthorizationTokenService.KeycloakAuthorizationRequest authorizationRequest = new AuthorizationTokenService.KeycloakAuthorizationRequest(session.getProvider(AuthorizationProvider.class), tokenManager, event, this.request, cors);
1080 
1081  authorizationRequest.setTicket(formParams.getFirst("ticket"));
1082  authorizationRequest.setClaimToken(claimToken);
1083  authorizationRequest.setClaimTokenFormat(claimTokenFormat);
1084  authorizationRequest.setPct(formParams.getFirst("pct"));
1085 
1086  String rpt = formParams.getFirst("rpt");
1087 
1088  if (rpt != null) {
1089  AccessToken accessToken = session.tokens().decode(rpt, AccessToken.class);
1090  if (accessToken == null) {
1091  throw new CorsErrorResponseException(cors, "invalid_rpt", "RPT signature is invalid", Status.FORBIDDEN);
1092  }
1093 
1094  authorizationRequest.setRpt(accessToken);
1095  }
1096 
1097  authorizationRequest.setScope(formParams.getFirst("scope"));
1098  authorizationRequest.setAudience(formParams.getFirst("audience"));
1099  authorizationRequest.setSubjectToken(formParams.getFirst("subject_token") != null ? formParams.getFirst("subject_token") : accessTokenString);
1100 
1101  String submitRequest = formParams.getFirst("submit_request");
1102 
1103  authorizationRequest.setSubmitRequest(submitRequest == null ? true : Boolean.valueOf(submitRequest));
1104 
1105  // permissions have a format like RESOURCE#SCOPE1,SCOPE2
1106  List<String> permissions = formParams.get("permission");
1107 
1108  if (permissions != null) {
1109  for (String permission : permissions) {
1110  String[] parts = permission.split("#");
1111  String resource = parts[0];
1112 
1113  if (parts.length == 1) {
1114  authorizationRequest.addPermission(resource);
1115  } else {
1116  String[] scopes = parts[1].split(",");
1117  authorizationRequest.addPermission(parts[0], scopes);
1118  }
1119  }
1120  }
1121 
1122  Metadata metadata = new Metadata();
1123 
1124  String responseIncludeResourceName = formParams.getFirst("response_include_resource_name");
1125 
1126  if (responseIncludeResourceName != null) {
1127  metadata.setIncludeResourceName(Boolean.parseBoolean(responseIncludeResourceName));
1128  }
1129 
1130  String responsePermissionsLimit = formParams.getFirst("response_permissions_limit");
1131 
1132  if (responsePermissionsLimit != null) {
1133  metadata.setLimit(Integer.parseInt(responsePermissionsLimit));
1134  }
1135 
1136  metadata.setResponseMode(formParams.getFirst("response_mode"));
1137 
1138  authorizationRequest.setMetadata(metadata);
1139 
1140  return AuthorizationTokenService.instance().authorize(authorizationRequest);
1141  }
HttpHeaders headers
Definition: TokenEndpoint.java:137
KeycloakSession session
Definition: TokenEndpoint.java:128
Cors cors
Definition: TokenEndpoint.java:150
ClientModel client
Definition: TokenEndpoint.java:117
void checkClient()
Definition: TokenEndpoint.java:227
Response clientCredentialsGrant()
Definition: TokenEndpoint.java:585
HttpRequest request
Definition: TokenEndpoint.java:131
MultivaluedMap< String, String > formParams
Definition: TokenEndpoint.java:116
final TokenManager tokenManager
Definition: TokenEndpoint.java:142
Set< String > allowedOrigins
Definition: Cors.java:62
final RealmModel realm
Definition: TokenEndpoint.java:143
final EventBuilder event
Definition: TokenEndpoint.java:144

◆ preflight()

Response org.keycloak.protocol.oidc.endpoints.TokenEndpoint.preflight ( )
inline
208  {
209  if (logger.isDebugEnabled()) {
210  logger.debugv("CORS preflight from: {0}", headers.getRequestHeaders().getFirst("Origin"));
211  }
212  return Cors.add(request, Response.ok()).auth().preflight().allowedMethods("POST", "OPTIONS").build();
213  }
HttpHeaders headers
Definition: TokenEndpoint.java:137
static final Logger logger
Definition: TokenEndpoint.java:115
HttpRequest request
Definition: TokenEndpoint.java:131

◆ processGrantRequest()

Response org.keycloak.protocol.oidc.endpoints.TokenEndpoint.processGrantRequest ( )
inline
159  {
160  cors = Cors.add(request).auth().allowedMethods("POST").auth().exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS);
161 
162  formParams = request.getDecodedFormParameters();
163  grantType = formParams.getFirst(OIDCLoginProtocol.GRANT_TYPE_PARAM);
164 
165  // https://tools.ietf.org/html/rfc6749#section-5.1
166  // The authorization server MUST include the HTTP "Cache-Control" response header field
167  // with a value of "no-store" as well as the "Pragma" response header field with a value of "no-cache".
168  MultivaluedMap<String, Object> outputHeaders = httpResponse.getOutputHeaders();
169  outputHeaders.putSingle("Cache-Control", "no-store");
170  outputHeaders.putSingle("Pragma", "no-cache");
171 
172  checkSsl();
173  checkRealm();
174  checkGrantType();
175 
176  if (!action.equals(Action.PERMISSION)) {
177  checkClient();
178  }
179 
180  switch (action) {
181  case AUTHORIZATION_CODE:
182  return codeToToken();
183  case REFRESH_TOKEN:
184  return refreshTokenGrant();
185  case PASSWORD:
187  case CLIENT_CREDENTIALS:
188  return clientCredentialsGrant();
189  case TOKEN_EXCHANGE:
190  return tokenExchange();
191  case PERMISSION:
192  return permissionGrant();
193  }
194 
195  throw new RuntimeException("Unknown action " + action);
196  }
static Cors add(HttpRequest request, ResponseBuilder response)
Definition: Cors.java:78
Cors cors
Definition: TokenEndpoint.java:150
String grantType
Definition: TokenEndpoint.java:148
Action action
Definition: TokenEndpoint.java:146
Response resourceOwnerPasswordCredentialsGrant()
Definition: TokenEndpoint.java:516
Response codeToToken()
Definition: TokenEndpoint.java:271
void checkRealm()
Definition: TokenEndpoint.java:221
void checkSsl()
Definition: TokenEndpoint.java:215
boolean auth
Definition: Cors.java:67
void checkGrantType()
Definition: TokenEndpoint.java:241
Response refreshTokenGrant()
Definition: TokenEndpoint.java:448
void checkClient()
Definition: TokenEndpoint.java:227
Response permissionGrant()
Definition: TokenEndpoint.java:1026
Response clientCredentialsGrant()
Definition: TokenEndpoint.java:585
HttpRequest request
Definition: TokenEndpoint.java:131
MultivaluedMap< String, String > formParams
Definition: TokenEndpoint.java:116
Response tokenExchange()
Definition: TokenEndpoint.java:657
HttpResponse httpResponse
Definition: TokenEndpoint.java:134

◆ refreshTokenGrant()

Response org.keycloak.protocol.oidc.endpoints.TokenEndpoint.refreshTokenGrant ( )
inline
448  {
449  String refreshToken = formParams.getFirst(OAuth2Constants.REFRESH_TOKEN);
450  if (refreshToken == null) {
451  throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_REQUEST, "No refresh token", Response.Status.BAD_REQUEST);
452  }
453 
454  AccessTokenResponse res;
455  try {
456  // KEYCLOAK-6771 Certificate Bound Token
457  TokenManager.RefreshResult result = tokenManager.refreshAccessToken(session, session.getContext().getUri(), clientConnection, realm, client, refreshToken, event, headers, request);
458  res = result.getResponse();
459 
460  if (!result.isOfflineToken()) {
461  UserSessionModel userSession = session.sessions().getUserSession(realm, res.getSessionState());
462  AuthenticatedClientSessionModel clientSession = userSession.getAuthenticatedClientSessionByClient(client.getId());
463  updateClientSession(clientSession);
464  updateUserSessionFromClientAuth(userSession);
465  }
466 
467  } catch (OAuthErrorException e) {
468  logger.trace(e.getMessage(), e);
469  // KEYCLOAK-6771 Certificate Bound Token
470  if (MtlsHoKTokenUtil.CERT_VERIFY_ERROR_DESC.equals(e.getDescription())) {
471  event.error(Errors.NOT_ALLOWED);
472  throw new CorsErrorResponseException(cors, e.getError(), e.getDescription(), Response.Status.UNAUTHORIZED);
473  } else {
474  event.error(Errors.INVALID_TOKEN);
475  throw new CorsErrorResponseException(cors, e.getError(), e.getDescription(), Response.Status.BAD_REQUEST);
476  }
477  }
478 
479  event.success();
480 
481  return cors.builder(Response.ok(res, MediaType.APPLICATION_JSON_TYPE)).build();
482  }
void updateClientSession(AuthenticatedClientSessionModel clientSession)
Definition: TokenEndpoint.java:484
HttpHeaders headers
Definition: TokenEndpoint.java:137
KeycloakSession session
Definition: TokenEndpoint.java:128
Cors cors
Definition: TokenEndpoint.java:150
ClientModel client
Definition: TokenEndpoint.java:117
AccessTokenResponse getResponse()
Definition: TokenManager.java:945
static final Logger logger
Definition: TokenEndpoint.java:115
HttpRequest request
Definition: TokenEndpoint.java:131
MultivaluedMap< String, String > formParams
Definition: TokenEndpoint.java:116
ResponseBuilder builder
Definition: Cors.java:61
ClientConnection clientConnection
Definition: TokenEndpoint.java:140
final TokenManager tokenManager
Definition: TokenEndpoint.java:142
RefreshResult refreshAccessToken(KeycloakSession session, UriInfo uriInfo, ClientConnection connection, RealmModel realm, ClientModel authorizedClient, String encodedRefreshToken, EventBuilder event, HttpHeaders headers, HttpRequest request)
Definition: TokenManager.java:264
final RealmModel realm
Definition: TokenEndpoint.java:143
void updateUserSessionFromClientAuth(UserSessionModel userSession)
Definition: TokenEndpoint.java:510
final EventBuilder event
Definition: TokenEndpoint.java:144

◆ resourceOwnerPasswordCredentialsGrant()

Response org.keycloak.protocol.oidc.endpoints.TokenEndpoint.resourceOwnerPasswordCredentialsGrant ( )
inline
516  {
517  event.detail(Details.AUTH_METHOD, "oauth_credentials");
518 
519  if (!client.isDirectAccessGrantsEnabled()) {
520  event.error(Errors.NOT_ALLOWED);
521  throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_GRANT, "Client not allowed for direct access grants", Response.Status.BAD_REQUEST);
522  }
523 
524  if (client.isConsentRequired()) {
525  event.error(Errors.CONSENT_DENIED);
526  throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_CLIENT, "Client requires user consent", Response.Status.BAD_REQUEST);
527  }
528  String scope = formParams.getFirst(OAuth2Constants.SCOPE);
529 
530  RootAuthenticationSessionModel rootAuthSession = new AuthenticationSessionManager(session).createAuthenticationSession(realm, false);
531  AuthenticationSessionModel authSession = rootAuthSession.createAuthenticationSession(client);
532 
533  authSession.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
534  authSession.setAction(AuthenticatedClientSessionModel.Action.AUTHENTICATE.name());
535  authSession.setClientNote(OIDCLoginProtocol.ISSUER, Urls.realmIssuer(session.getContext().getUri().getBaseUri(), realm.getName()));
536  authSession.setClientNote(OIDCLoginProtocol.SCOPE_PARAM, scope);
537 
538  AuthenticationFlowModel flow = AuthenticationFlowResolver.resolveDirectGrantFlow(authSession);
539  String flowId = flow.getId();
540  AuthenticationProcessor processor = new AuthenticationProcessor();
541  processor.setAuthenticationSession(authSession)
542  .setFlowId(flowId)
543  .setConnection(clientConnection)
544  .setEventBuilder(event)
545  .setRealm(realm)
546  .setSession(session)
547  .setUriInfo(session.getContext().getUri())
548  .setRequest(request);
549  Response challenge = processor.authenticateOnly();
550  if (challenge != null) {
552  return challenge;
553  }
554  processor.evaluateRequiredActionTriggers();
555  UserModel user = authSession.getAuthenticatedUser();
556  if (user.getRequiredActions() != null && user.getRequiredActions().size() > 0) {
557  event.error(Errors.RESOLVE_REQUIRED_ACTIONS);
558  throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_GRANT, "Invalid user credentials", Response.Status.UNAUTHORIZED);
559 
560  }
561 
562  AuthenticationManager.setClientScopesInSession(authSession);
563 
564  ClientSessionContext clientSessionCtx = processor.attachSession();
565  UserSessionModel userSession = processor.getUserSession();
566  updateUserSessionFromClientAuth(userSession);
567 
568  TokenManager.AccessTokenResponseBuilder responseBuilder = tokenManager.responseBuilder(realm, client, event, session, userSession, clientSessionCtx)
571 
572  String scopeParam = clientSessionCtx.getClientSession().getNote(OAuth2Constants.SCOPE);
573  if (TokenUtil.isOIDCRequest(scopeParam)) {
574  responseBuilder.generateIDToken();
575  }
576 
577  AccessTokenResponse res = responseBuilder.build();
578 
579 
580  event.success();
581 
582  return cors.builder(Response.ok(res, MediaType.APPLICATION_JSON_TYPE)).build();
583  }
AccessTokenResponse build()
Definition: TokenManager.java:869
KeycloakSession session
Definition: TokenEndpoint.java:128
Cors cors
Definition: TokenEndpoint.java:150
AccessTokenResponseBuilder generateRefreshToken()
Definition: TokenManager.java:799
AccessTokenResponseBuilder generateIDToken()
Definition: TokenManager.java:831
ClientModel client
Definition: TokenEndpoint.java:117
Response build()
Definition: Cors.java:137
AccessTokenResponseBuilder responseBuilder(RealmModel realm, ClientModel client, EventBuilder event, KeycloakSession session, UserSessionModel userSession, ClientSessionContext clientSessionCtx)
Definition: TokenManager.java:740
HttpRequest request
Definition: TokenEndpoint.java:131
MultivaluedMap< String, String > formParams
Definition: TokenEndpoint.java:116
ResponseBuilder builder
Definition: Cors.java:61
ClientConnection clientConnection
Definition: TokenEndpoint.java:140
final TokenManager tokenManager
Definition: TokenEndpoint.java:142
HttpResponse httpResponse
Definition: TokenEndpoint.java:134
final RealmModel realm
Definition: TokenEndpoint.java:143
void updateUserSessionFromClientAuth(UserSessionModel userSession)
Definition: TokenEndpoint.java:510
AccessTokenResponseBuilder generateAccessToken()
Definition: TokenManager.java:793
final EventBuilder event
Definition: TokenEndpoint.java:144

◆ tokenExchange()

Response org.keycloak.protocol.oidc.endpoints.TokenEndpoint.tokenExchange ( )
inline
657  {
658  ProfileHelper.requireFeature(Profile.Feature.TOKEN_EXCHANGE);
659 
660  event.detail(Details.AUTH_METHOD, "token_exchange");
661  event.client(client);
662 
663  UserModel tokenUser = null;
664  UserSessionModel tokenSession = null;
665  AccessToken token = null;
666 
667  String subjectToken = formParams.getFirst(OAuth2Constants.SUBJECT_TOKEN);
668  if (subjectToken != null) {
669  String subjectTokenType = formParams.getFirst(OAuth2Constants.SUBJECT_TOKEN_TYPE);
670  String realmIssuerUrl = Urls.realmIssuer(session.getContext().getUri().getBaseUri(), realm.getName());
671  String subjectIssuer = formParams.getFirst(OAuth2Constants.SUBJECT_ISSUER);
672 
673  if (subjectIssuer == null && OAuth2Constants.JWT_TOKEN_TYPE.equals(subjectTokenType)) {
674  try {
675  JWSInput jws = new JWSInput(subjectToken);
676  JsonWebToken jwt = jws.readJsonContent(JsonWebToken.class);
677  subjectIssuer = jwt.getIssuer();
678  } catch (JWSInputException e) {
679  event.detail(Details.REASON, "unable to parse jwt subject_token");
680  event.error(Errors.INVALID_TOKEN);
681  throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_TOKEN, "Invalid token type, must be access token", Response.Status.BAD_REQUEST);
682 
683  }
684  }
685 
686  if (subjectIssuer != null && !realmIssuerUrl.equals(subjectIssuer)) {
687  event.detail(OAuth2Constants.SUBJECT_ISSUER, subjectIssuer);
688  return exchangeExternalToken(subjectIssuer, subjectToken);
689 
690  }
691 
692  if (subjectTokenType != null && !subjectTokenType.equals(OAuth2Constants.ACCESS_TOKEN_TYPE)) {
693  event.detail(Details.REASON, "subject_token supports access tokens only");
694  event.error(Errors.INVALID_TOKEN);
695  throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_TOKEN, "Invalid token type, must be access token", Response.Status.BAD_REQUEST);
696 
697  }
698 
699  AuthenticationManager.AuthResult authResult = AuthenticationManager.verifyIdentityToken(session, realm, session.getContext().getUri(), clientConnection, true, true, false, subjectToken, headers);
700  if (authResult == null) {
701  event.detail(Details.REASON, "subject_token validation failure");
702  event.error(Errors.INVALID_TOKEN);
703  throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_TOKEN, "Invalid token", Response.Status.BAD_REQUEST);
704  }
705 
706  tokenUser = authResult.getUser();
707  tokenSession = authResult.getSession();
708  token = authResult.getToken();
709  }
710 
711  String requestedSubject = formParams.getFirst(OAuth2Constants.REQUESTED_SUBJECT);
712  if (requestedSubject != null) {
713  event.detail(Details.REQUESTED_SUBJECT, requestedSubject);
714  UserModel requestedUser = session.users().getUserByUsername(requestedSubject, realm);
715  if (requestedUser == null) {
716  requestedUser = session.users().getUserById(requestedSubject, realm);
717  }
718 
719  if (requestedUser == null) {
720  // We always returned access denied to avoid username fishing
721  event.detail(Details.REASON, "requested_subject not found");
722  event.error(Errors.NOT_ALLOWED);
723  throw new CorsErrorResponseException(cors, OAuthErrorException.ACCESS_DENIED, "Client not allowed to exchange", Response.Status.FORBIDDEN);
724 
725  }
726 
727  if (token != null) {
728  event.detail(Details.IMPERSONATOR, tokenUser.getUsername());
729  // for this case, the user represented by the token, must have permission to impersonate.
730  AdminAuth auth = new AdminAuth(realm, token, tokenUser, client);
731  if (!AdminPermissions.evaluator(session, realm, auth).users().canImpersonate(requestedUser)) {
732  event.detail(Details.REASON, "subject not allowed to impersonate");
733  event.error(Errors.NOT_ALLOWED);
734  throw new CorsErrorResponseException(cors, OAuthErrorException.ACCESS_DENIED, "Client not allowed to exchange", Response.Status.FORBIDDEN);
735  }
736 
737  } else {
738  // no token is being exchanged, this is a direct exchange. Client must be authenticated, not public, and must be allowed
739  // to impersonate
740  if (client.isPublicClient()) {
741  event.detail(Details.REASON, "public clients not allowed");
742  event.error(Errors.NOT_ALLOWED);
743  throw new CorsErrorResponseException(cors, OAuthErrorException.ACCESS_DENIED, "Client not allowed to exchange", Response.Status.FORBIDDEN);
744 
745  }
746  if (!AdminPermissions.management(session, realm).users().canClientImpersonate(client, requestedUser)) {
747  event.detail(Details.REASON, "client not allowed to impersonate");
748  event.error(Errors.NOT_ALLOWED);
749  throw new CorsErrorResponseException(cors, OAuthErrorException.ACCESS_DENIED, "Client not allowed to exchange", Response.Status.FORBIDDEN);
750  }
751  }
752 
753  tokenUser = requestedUser;
754  tokenSession = session.sessions().createUserSession(realm, requestedUser, requestedUser.getUsername(), clientConnection.getRemoteAddr(), "impersonate", false, null, null);
755  }
756 
757  String requestedIssuer = formParams.getFirst(OAuth2Constants.REQUESTED_ISSUER);
758 
759  if (requestedIssuer == null) {
760  return exchangeClientToClient(tokenUser, tokenSession);
761  } else {
762  try {
763  return exchangeToIdentityProvider(tokenUser, tokenSession, requestedIssuer);
764  } finally {
765  if (subjectToken == null) { // we are naked! So need to clean up user session
766  try {
767  session.sessions().removeUserSession(realm, tokenSession);
768  } catch (Exception ignore) {
769 
770  }
771  }
772  }
773  }
774  }
HttpHeaders headers
Definition: TokenEndpoint.java:137
KeycloakSession session
Definition: TokenEndpoint.java:128
Cors cors
Definition: TokenEndpoint.java:150
Response exchangeToIdentityProvider(UserModel targetUser, UserSessionModel targetUserSession, String requestedIssuer)
Definition: TokenEndpoint.java:776
Response exchangeClientToClient(UserModel targetUser, UserSessionModel targetUserSession)
Definition: TokenEndpoint.java:801
ClientModel client
Definition: TokenEndpoint.java:117
Response exchangeExternalToken(String issuer, String subjectToken)
Definition: TokenEndpoint.java:875
MultivaluedMap< String, String > formParams
Definition: TokenEndpoint.java:116
ClientConnection clientConnection
Definition: TokenEndpoint.java:140
final RealmModel realm
Definition: TokenEndpoint.java:143

◆ updateClientSession()

void org.keycloak.protocol.oidc.endpoints.TokenEndpoint.updateClientSession ( AuthenticatedClientSessionModel  clientSession)
inlineprivate
484  {
485 
486  if(clientSession == null) {
487  ServicesLogger.LOGGER.clientSessionNull();
488  return;
489  }
490 
491  String adapterSessionId = formParams.getFirst(AdapterConstants.CLIENT_SESSION_STATE);
492  if (adapterSessionId != null) {
493  String adapterSessionHost = formParams.getFirst(AdapterConstants.CLIENT_SESSION_HOST);
494  logger.debugf("Adapter Session '%s' saved in ClientSession for client '%s'. Host is '%s'", adapterSessionId, client.getClientId(), adapterSessionHost);
495 
496  event.detail(AdapterConstants.CLIENT_SESSION_STATE, adapterSessionId);
497  String oldClientSessionState = clientSession.getNote(AdapterConstants.CLIENT_SESSION_STATE);
498  if (!adapterSessionId.equals(oldClientSessionState)) {
499  clientSession.setNote(AdapterConstants.CLIENT_SESSION_STATE, adapterSessionId);
500  }
501 
502  event.detail(AdapterConstants.CLIENT_SESSION_HOST, adapterSessionHost);
503  String oldClientSessionHost = clientSession.getNote(AdapterConstants.CLIENT_SESSION_HOST);
504  if (!Objects.equals(adapterSessionHost, oldClientSessionHost)) {
505  clientSession.setNote(AdapterConstants.CLIENT_SESSION_HOST, adapterSessionHost);
506  }
507  }
508  }
ClientModel client
Definition: TokenEndpoint.java:117
static final Logger logger
Definition: TokenEndpoint.java:115
MultivaluedMap< String, String > formParams
Definition: TokenEndpoint.java:116

◆ updateUserSessionFromClientAuth()

void org.keycloak.protocol.oidc.endpoints.TokenEndpoint.updateUserSessionFromClientAuth ( UserSessionModel  userSession)
inlineprivate
510  {
511  for (Map.Entry<String, String> attr : clientAuthAttributes.entrySet()) {
512  userSession.setNote(attr.getKey(), attr.getValue());
513  }
514  }
Map< String, String > clientAuthAttributes
Definition: TokenEndpoint.java:118

メンバ詳解

◆ action

Action org.keycloak.protocol.oidc.endpoints.TokenEndpoint.action
private

◆ client

ClientModel org.keycloak.protocol.oidc.endpoints.TokenEndpoint.client
private

◆ clientAuthAttributes

Map<String, String> org.keycloak.protocol.oidc.endpoints.TokenEndpoint.clientAuthAttributes
private

◆ clientConnection

ClientConnection org.keycloak.protocol.oidc.endpoints.TokenEndpoint.clientConnection
private

◆ cors

Cors org.keycloak.protocol.oidc.endpoints.TokenEndpoint.cors
private

◆ event

final EventBuilder org.keycloak.protocol.oidc.endpoints.TokenEndpoint.event
private

◆ formParams

MultivaluedMap<String, String> org.keycloak.protocol.oidc.endpoints.TokenEndpoint.formParams
private

◆ grantType

String org.keycloak.protocol.oidc.endpoints.TokenEndpoint.grantType
private

◆ headers

HttpHeaders org.keycloak.protocol.oidc.endpoints.TokenEndpoint.headers
private

◆ httpResponse

HttpResponse org.keycloak.protocol.oidc.endpoints.TokenEndpoint.httpResponse
private

◆ logger

final Logger org.keycloak.protocol.oidc.endpoints.TokenEndpoint.logger = Logger.getLogger(TokenEndpoint.class)
staticprivate

◆ realm

final RealmModel org.keycloak.protocol.oidc.endpoints.TokenEndpoint.realm
private

◆ request

HttpRequest org.keycloak.protocol.oidc.endpoints.TokenEndpoint.request
private

◆ session

KeycloakSession org.keycloak.protocol.oidc.endpoints.TokenEndpoint.session
private

◆ tokenManager

final TokenManager org.keycloak.protocol.oidc.endpoints.TokenEndpoint.tokenManager
private

◆ VALID_CODE_VERIFIER_PATTERN

final Pattern org.keycloak.protocol.oidc.endpoints.TokenEndpoint.VALID_CODE_VERIFIER_PATTERN = Pattern.compile("^[0-9a-zA-Z\\-\\.~_]+$")
staticprivate

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