use of org.keycloak.services.CorsErrorResponseException in project keycloak by keycloak.
the class DefaultTokenExchangeProvider method tokenExchange.
protected Response tokenExchange() {
UserModel tokenUser = null;
UserSessionModel tokenSession = null;
AccessToken token = null;
String subjectToken = formParams.getFirst(OAuth2Constants.SUBJECT_TOKEN);
if (subjectToken != null) {
String subjectTokenType = formParams.getFirst(OAuth2Constants.SUBJECT_TOKEN_TYPE);
String realmIssuerUrl = Urls.realmIssuer(session.getContext().getUri().getBaseUri(), realm.getName());
String subjectIssuer = formParams.getFirst(OAuth2Constants.SUBJECT_ISSUER);
if (subjectIssuer == null && OAuth2Constants.JWT_TOKEN_TYPE.equals(subjectTokenType)) {
try {
JWSInput jws = new JWSInput(subjectToken);
JsonWebToken jwt = jws.readJsonContent(JsonWebToken.class);
subjectIssuer = jwt.getIssuer();
} catch (JWSInputException e) {
event.detail(Details.REASON, "unable to parse jwt subject_token");
event.error(Errors.INVALID_TOKEN);
throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_TOKEN, "Invalid token type, must be access token", Response.Status.BAD_REQUEST);
}
}
if (subjectIssuer != null && !realmIssuerUrl.equals(subjectIssuer)) {
event.detail(OAuth2Constants.SUBJECT_ISSUER, subjectIssuer);
return exchangeExternalToken(subjectIssuer, subjectToken);
}
if (subjectTokenType != null && !subjectTokenType.equals(OAuth2Constants.ACCESS_TOKEN_TYPE)) {
event.detail(Details.REASON, "subject_token supports access tokens only");
event.error(Errors.INVALID_TOKEN);
throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_TOKEN, "Invalid token type, must be access token", Response.Status.BAD_REQUEST);
}
AuthenticationManager.AuthResult authResult = AuthenticationManager.verifyIdentityToken(session, realm, session.getContext().getUri(), clientConnection, true, true, null, false, subjectToken, headers);
if (authResult == null) {
event.detail(Details.REASON, "subject_token validation failure");
event.error(Errors.INVALID_TOKEN);
throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_TOKEN, "Invalid token", Response.Status.BAD_REQUEST);
}
tokenUser = authResult.getUser();
tokenSession = authResult.getSession();
token = authResult.getToken();
}
String requestedSubject = formParams.getFirst(OAuth2Constants.REQUESTED_SUBJECT);
if (requestedSubject != null) {
event.detail(Details.REQUESTED_SUBJECT, requestedSubject);
UserModel requestedUser = session.users().getUserByUsername(realm, requestedSubject);
if (requestedUser == null) {
requestedUser = session.users().getUserById(realm, requestedSubject);
}
if (requestedUser == null) {
// We always returned access denied to avoid username fishing
event.detail(Details.REASON, "requested_subject not found");
event.error(Errors.NOT_ALLOWED);
throw new CorsErrorResponseException(cors, OAuthErrorException.ACCESS_DENIED, "Client not allowed to exchange", Response.Status.FORBIDDEN);
}
if (token != null) {
event.detail(Details.IMPERSONATOR, tokenUser.getUsername());
// for this case, the user represented by the token, must have permission to impersonate.
AdminAuth auth = new AdminAuth(realm, token, tokenUser, client);
if (!AdminPermissions.evaluator(session, realm, auth).users().canImpersonate(requestedUser)) {
event.detail(Details.REASON, "subject not allowed to impersonate");
event.error(Errors.NOT_ALLOWED);
throw new CorsErrorResponseException(cors, OAuthErrorException.ACCESS_DENIED, "Client not allowed to exchange", Response.Status.FORBIDDEN);
}
} else {
// to impersonate
if (client.isPublicClient()) {
event.detail(Details.REASON, "public clients not allowed");
event.error(Errors.NOT_ALLOWED);
throw new CorsErrorResponseException(cors, OAuthErrorException.ACCESS_DENIED, "Client not allowed to exchange", Response.Status.FORBIDDEN);
}
if (!AdminPermissions.management(session, realm).users().canClientImpersonate(client, requestedUser)) {
event.detail(Details.REASON, "client not allowed to impersonate");
event.error(Errors.NOT_ALLOWED);
throw new CorsErrorResponseException(cors, OAuthErrorException.ACCESS_DENIED, "Client not allowed to exchange", Response.Status.FORBIDDEN);
}
}
tokenSession = session.sessions().createUserSession(realm, requestedUser, requestedUser.getUsername(), clientConnection.getRemoteAddr(), "impersonate", false, null, null);
if (tokenUser != null) {
tokenSession.setNote(IMPERSONATOR_ID.toString(), tokenUser.getId());
tokenSession.setNote(IMPERSONATOR_USERNAME.toString(), tokenUser.getUsername());
}
tokenUser = requestedUser;
}
String requestedIssuer = formParams.getFirst(OAuth2Constants.REQUESTED_ISSUER);
if (requestedIssuer == null) {
return exchangeClientToClient(tokenUser, tokenSession);
} else {
try {
return exchangeToIdentityProvider(tokenUser, tokenSession, requestedIssuer);
} finally {
if (subjectToken == null) {
// we are naked! So need to clean up user session
try {
session.sessions().removeUserSession(realm, tokenSession);
} catch (Exception ignore) {
}
}
}
}
}
use of org.keycloak.services.CorsErrorResponseException in project keycloak by keycloak.
the class DefaultTokenExchangeProvider method exchangeToIdentityProvider.
protected Response exchangeToIdentityProvider(UserModel targetUser, UserSessionModel targetUserSession, String requestedIssuer) {
event.detail(Details.REQUESTED_ISSUER, requestedIssuer);
IdentityProviderModel providerModel = realm.getIdentityProviderByAlias(requestedIssuer);
if (providerModel == null) {
event.detail(Details.REASON, "unknown requested_issuer");
event.error(Errors.UNKNOWN_IDENTITY_PROVIDER);
throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_REQUEST, "Invalid issuer", Response.Status.BAD_REQUEST);
}
IdentityProvider provider = IdentityBrokerService.getIdentityProvider(session, realm, requestedIssuer);
if (!(provider instanceof ExchangeTokenToIdentityProviderToken)) {
event.detail(Details.REASON, "exchange unsupported by requested_issuer");
event.error(Errors.UNKNOWN_IDENTITY_PROVIDER);
throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_REQUEST, "Issuer does not support token exchange", Response.Status.BAD_REQUEST);
}
if (!AdminPermissions.management(session, realm).idps().canExchangeTo(client, providerModel)) {
event.detail(Details.REASON, "client not allowed to exchange for requested_issuer");
event.error(Errors.NOT_ALLOWED);
throw new CorsErrorResponseException(cors, OAuthErrorException.ACCESS_DENIED, "Client not allowed to exchange", Response.Status.FORBIDDEN);
}
Response response = ((ExchangeTokenToIdentityProviderToken) provider).exchangeFromToken(session.getContext().getUri(), event, client, targetUserSession, targetUser, formParams);
return cors.builder(Response.fromResponse(response)).build();
}
use of org.keycloak.services.CorsErrorResponseException in project keycloak by keycloak.
the class DefaultTokenExchangeProvider method importUserFromExternalIdentity.
protected UserModel importUserFromExternalIdentity(BrokeredIdentityContext context) {
IdentityProviderModel identityProviderConfig = context.getIdpConfig();
String providerId = identityProviderConfig.getAlias();
// do we need this?
// AuthenticationSessionModel authenticationSession = clientCode.getClientSession();
// context.setAuthenticationSession(authenticationSession);
// session.getContext().setClient(authenticationSession.getClient());
context.getIdp().preprocessFederatedIdentity(session, realm, context);
Set<IdentityProviderMapperModel> mappers = realm.getIdentityProviderMappersByAliasStream(context.getIdpConfig().getAlias()).collect(Collectors.toSet());
KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory();
for (IdentityProviderMapperModel mapper : mappers) {
IdentityProviderMapper target = (IdentityProviderMapper) sessionFactory.getProviderFactory(IdentityProviderMapper.class, mapper.getIdentityProviderMapper());
target.preprocessFederatedIdentity(session, realm, mapper, context);
}
FederatedIdentityModel federatedIdentityModel = new FederatedIdentityModel(providerId, context.getId(), context.getUsername(), context.getToken());
UserModel user = this.session.users().getUserByFederatedIdentity(realm, federatedIdentityModel);
if (user == null) {
logger.debugf("Federated user not found for provider '%s' and broker username '%s'.", providerId, context.getUsername());
String username = context.getModelUsername();
if (username == null) {
if (this.realm.isRegistrationEmailAsUsername() && !Validation.isBlank(context.getEmail())) {
username = context.getEmail();
} else if (context.getUsername() == null) {
username = context.getIdpConfig().getAlias() + "." + context.getId();
} else {
username = context.getUsername();
}
}
username = username.trim();
context.setModelUsername(username);
if (context.getEmail() != null && !realm.isDuplicateEmailsAllowed()) {
UserModel existingUser = session.users().getUserByEmail(realm, context.getEmail());
if (existingUser != null) {
event.error(Errors.FEDERATED_IDENTITY_EXISTS);
throw new CorsErrorResponseException(cors, Errors.INVALID_TOKEN, "User already exists", Response.Status.BAD_REQUEST);
}
}
UserModel existingUser = session.users().getUserByUsername(realm, username);
if (existingUser != null) {
event.error(Errors.FEDERATED_IDENTITY_EXISTS);
throw new CorsErrorResponseException(cors, Errors.INVALID_TOKEN, "User already exists", Response.Status.BAD_REQUEST);
}
user = session.users().addUser(realm, username);
user.setEnabled(true);
user.setEmail(context.getEmail());
user.setFirstName(context.getFirstName());
user.setLastName(context.getLastName());
federatedIdentityModel = new FederatedIdentityModel(context.getIdpConfig().getAlias(), context.getId(), context.getUsername(), context.getToken());
session.users().addFederatedIdentity(realm, user, federatedIdentityModel);
context.getIdp().importNewUser(session, realm, user, context);
for (IdentityProviderMapperModel mapper : mappers) {
IdentityProviderMapper target = (IdentityProviderMapper) sessionFactory.getProviderFactory(IdentityProviderMapper.class, mapper.getIdentityProviderMapper());
target.importNewUser(session, realm, user, mapper, context);
}
if (context.getIdpConfig().isTrustEmail() && !Validation.isBlank(user.getEmail())) {
logger.debugf("Email verified automatically after registration of user '%s' through Identity provider '%s' ", user.getUsername(), context.getIdpConfig().getAlias());
user.setEmailVerified(true);
}
} else {
if (!user.isEnabled()) {
event.error(Errors.USER_DISABLED);
throw new CorsErrorResponseException(cors, Errors.INVALID_TOKEN, "Invalid Token", Response.Status.BAD_REQUEST);
}
String bruteForceError = getDisabledByBruteForceEventError(session.getProvider(BruteForceProtector.class), session, realm, user);
if (bruteForceError != null) {
event.error(bruteForceError);
throw new CorsErrorResponseException(cors, Errors.INVALID_TOKEN, "Invalid Token", Response.Status.BAD_REQUEST);
}
context.getIdp().updateBrokeredUser(session, realm, user, context);
for (IdentityProviderMapperModel mapper : mappers) {
IdentityProviderMapper target = (IdentityProviderMapper) sessionFactory.getProviderFactory(IdentityProviderMapper.class, mapper.getIdentityProviderMapper());
IdentityProviderMapperSyncModeDelegate.delegateUpdateBrokeredUser(session, realm, user, mapper, context, target);
}
}
return user;
}
use of org.keycloak.services.CorsErrorResponseException in project keycloak by keycloak.
the class DefaultTokenExchangeProvider method exchangeExternalToken.
protected Response exchangeExternalToken(String issuer, String subjectToken) {
AtomicReference<ExchangeExternalToken> externalIdp = new AtomicReference<>(null);
AtomicReference<IdentityProviderModel> externalIdpModel = new AtomicReference<>(null);
realm.getIdentityProvidersStream().filter(idpModel -> {
IdentityProviderFactory factory = IdentityBrokerService.getIdentityProviderFactory(session, idpModel);
IdentityProvider idp = factory.create(session, idpModel);
if (idp instanceof ExchangeExternalToken) {
ExchangeExternalToken external = (ExchangeExternalToken) idp;
if (idpModel.getAlias().equals(issuer) || external.isIssuer(issuer, formParams)) {
externalIdp.set(external);
externalIdpModel.set(idpModel);
return true;
}
}
return false;
}).findFirst();
if (externalIdp.get() == null) {
event.error(Errors.INVALID_ISSUER);
throw new CorsErrorResponseException(cors, Errors.INVALID_ISSUER, "Invalid " + OAuth2Constants.SUBJECT_ISSUER + " parameter", Response.Status.BAD_REQUEST);
}
if (!AdminPermissions.management(session, realm).idps().canExchangeTo(client, externalIdpModel.get())) {
event.detail(Details.REASON, "client not allowed to exchange subject_issuer");
event.error(Errors.NOT_ALLOWED);
throw new CorsErrorResponseException(cors, OAuthErrorException.ACCESS_DENIED, "Client not allowed to exchange", Response.Status.FORBIDDEN);
}
BrokeredIdentityContext context = externalIdp.get().exchangeExternal(event, formParams);
if (context == null) {
event.error(Errors.INVALID_ISSUER);
throw new CorsErrorResponseException(cors, Errors.INVALID_ISSUER, "Invalid " + OAuth2Constants.SUBJECT_ISSUER + " parameter", Response.Status.BAD_REQUEST);
}
UserModel user = importUserFromExternalIdentity(context);
UserSessionModel userSession = session.sessions().createUserSession(realm, user, user.getUsername(), clientConnection.getRemoteAddr(), "external-exchange", false, null, null);
externalIdp.get().exchangeExternalComplete(userSession, context, formParams);
// this must exist so that we can obtain access token from user session if idp's store tokens is off
userSession.setNote(IdentityProvider.EXTERNAL_IDENTITY_PROVIDER, externalIdpModel.get().getAlias());
userSession.setNote(IdentityProvider.FEDERATED_ACCESS_TOKEN, subjectToken);
return exchangeClientToClient(user, userSession);
}
use of org.keycloak.services.CorsErrorResponseException in project keycloak by keycloak.
the class AuthorizationTokenService method resolveRequestedScopes.
private Set<Scope> resolveRequestedScopes(KeycloakAuthorizationRequest request, ResourceServer resourceServer, ScopeStore scopeStore, Permission permission) {
String clientAdditionalScopes = request.getScope();
Set<String> requestedScopes = permission.getScopes();
if (permission.getScopes() == null) {
requestedScopes = new HashSet<>();
}
if (clientAdditionalScopes != null) {
requestedScopes.addAll(Arrays.asList(clientAdditionalScopes.split(" ")));
}
Set<Scope> requestedScopesModel = requestedScopes.stream().map(s -> scopeStore.findByName(s, resourceServer.getId())).filter(Objects::nonNull).collect(Collectors.toSet());
if (!requestedScopes.isEmpty() && requestedScopesModel.isEmpty()) {
CorsErrorResponseException invalidScopeException = new CorsErrorResponseException(request.getCors(), "invalid_scope", "One of the given scopes " + permission.getScopes() + " is invalid", Status.BAD_REQUEST);
fireErrorEvent(request.getEvent(), Errors.INVALID_REQUEST, invalidScopeException);
throw invalidScopeException;
}
return requestedScopesModel;
}
Aggregations