use of org.keycloak.jose.jws.JWSInputException in project keycloak by keycloak.
the class AbstractShowTokensServlet method renderTokens.
protected String renderTokens(HttpServletRequest req) throws ServletException, IOException {
RefreshableKeycloakSecurityContext ctx = (RefreshableKeycloakSecurityContext) req.getAttribute(KeycloakSecurityContext.class.getName());
String accessTokenPretty = JsonSerialization.writeValueAsPrettyString(ctx.getToken());
RefreshToken refreshToken;
try {
refreshToken = new JWSInput(ctx.getRefreshToken()).readJsonContent(RefreshToken.class);
} catch (JWSInputException e) {
throw new IOException(e);
}
String refreshTokenPretty = JsonSerialization.writeValueAsPrettyString(refreshToken);
return new StringBuilder("<span id=\"accessToken\">" + accessTokenPretty + "</span>").append("<span id=\"refreshToken\">" + refreshTokenPretty + "</span>").append("<span id=\"accessTokenString\">" + ctx.getTokenString() + "</span>").append("<span id=\"refreshTokenString\">" + ctx.getRefreshToken() + "</span>").toString();
}
use of org.keycloak.jose.jws.JWSInputException in project keycloak by keycloak.
the class TestingOIDCEndpointsApplicationResource method requestAuthenticationChannel.
@POST
@Path("/request-authentication-channel")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public Response requestAuthenticationChannel(@Context HttpHeaders headers, AuthenticationChannelRequest request) {
String rawBearerToken = AppAuthManager.extractAuthorizationHeaderToken(headers);
AccessToken bearerToken;
try {
bearerToken = new JWSInput(rawBearerToken).readJsonContent(AccessToken.class);
} catch (JWSInputException e) {
throw new RuntimeException("Failed to parse bearer token", e);
}
// required
String authenticationChannelId = bearerToken.getId();
if (authenticationChannelId == null)
throw new BadRequestException("missing parameter : " + HttpAuthenticationChannelProvider.AUTHENTICATION_CHANNEL_ID);
String loginHint = request.getLoginHint();
if (loginHint == null)
throw new BadRequestException("missing parameter : " + CibaGrantType.LOGIN_HINT);
if (request.getConsentRequired() == null)
throw new BadRequestException("missing parameter : " + CibaGrantType.IS_CONSENT_REQUIRED);
String scope = request.getScope();
if (scope == null)
throw new BadRequestException("missing parameter : " + OAuth2Constants.SCOPE);
// optional
// for testing purpose
String bindingMessage = request.getBindingMessage();
if (bindingMessage != null && bindingMessage.equals("GODOWN"))
throw new BadRequestException("intentional error : GODOWN");
// only one CIBA flow without binding_message can be accepted per test method by this test mechanism.
if (bindingMessage == null)
bindingMessage = ChannelRequestDummyKey;
authenticationChannelRequests.put(bindingMessage, new TestAuthenticationChannelRequest(request, rawBearerToken));
return Response.status(Status.CREATED).build();
}
use of org.keycloak.jose.jws.JWSInputException in project keycloak by keycloak.
the class TokenEndpoint method permissionGrant.
public Response permissionGrant() {
event.detail(Details.AUTH_METHOD, "oauth_credentials");
String accessTokenString = null;
String authorizationHeader = headers.getRequestHeaders().getFirst(HttpHeaders.AUTHORIZATION);
if (authorizationHeader != null && authorizationHeader.toLowerCase().startsWith("bearer")) {
accessTokenString = new AppAuthManager().extractAuthorizationHeaderToken(headers);
}
// public clients don't have secret and should be able to obtain a RPT by providing an access token previously issued by the server
if (accessTokenString != null) {
AccessToken accessToken = Tokens.getAccessToken(session);
if (accessToken == null) {
try {
// In case the access token is invalid because it's expired or the user is disabled, identify the client
// from the access token anyway in order to set correct CORS headers.
AccessToken invalidToken = new JWSInput(accessTokenString).readJsonContent(AccessToken.class);
ClientModel client = realm.getClientByClientId(invalidToken.getIssuedFor());
cors.allowedOrigins(session, client);
event.client(client);
} catch (JWSInputException ignore) {
}
event.error(Errors.INVALID_TOKEN);
throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_GRANT, "Invalid bearer token", Status.UNAUTHORIZED);
}
ClientModel client = realm.getClientByClientId(accessToken.getIssuedFor());
session.getContext().setClient(client);
cors.allowedOrigins(session, client);
event.client(client);
}
String claimToken = null;
// claim_token is optional, if provided we just grab it from the request
if (formParams.containsKey("claim_token")) {
claimToken = formParams.get("claim_token").get(0);
}
String claimTokenFormat = formParams.getFirst("claim_token_format");
if (claimToken != null && claimTokenFormat == null) {
claimTokenFormat = AuthorizationTokenService.CLAIM_TOKEN_FORMAT_ID_TOKEN;
}
String subjectToken = formParams.getFirst("subject_token");
if (accessTokenString == null) {
// in case no bearer token is provided, we force client authentication
checkClient();
// 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
if (AuthorizationTokenService.CLAIM_TOKEN_FORMAT_ID_TOKEN.equalsIgnoreCase(claimTokenFormat)) {
accessTokenString = claimToken;
} else if (subjectToken != null) {
accessTokenString = subjectToken;
} else {
// Clients need to authenticate in order to obtain a RPT from the server.
// In order to support cases where the client is obtaining permissions on its on behalf, we issue a temporary access token
accessTokenString = AccessTokenResponse.class.cast(clientCredentialsGrant().getEntity()).getToken();
}
}
AuthorizationTokenService.KeycloakAuthorizationRequest authorizationRequest = new AuthorizationTokenService.KeycloakAuthorizationRequest(session.getProvider(AuthorizationProvider.class), tokenManager, event, this.request, cors, clientConnection);
authorizationRequest.setTicket(formParams.getFirst("ticket"));
authorizationRequest.setClaimToken(claimToken);
authorizationRequest.setClaimTokenFormat(claimTokenFormat);
authorizationRequest.setPct(formParams.getFirst("pct"));
String rpt = formParams.getFirst("rpt");
if (rpt != null) {
AccessToken accessToken = session.tokens().decode(rpt, AccessToken.class);
if (accessToken == null) {
event.error(Errors.INVALID_REQUEST);
throw new CorsErrorResponseException(cors, "invalid_rpt", "RPT signature is invalid", Status.FORBIDDEN);
}
authorizationRequest.setRpt(accessToken);
}
authorizationRequest.setScope(formParams.getFirst("scope"));
String audienceParam = formParams.getFirst("audience");
authorizationRequest.setAudience(audienceParam);
authorizationRequest.setSubjectToken(accessTokenString);
event.detail(Details.AUDIENCE, audienceParam);
String submitRequest = formParams.getFirst("submit_request");
authorizationRequest.setSubmitRequest(submitRequest == null ? true : Boolean.valueOf(submitRequest));
// permissions have a format like RESOURCE#SCOPE1,SCOPE2
List<String> permissions = formParams.get("permission");
if (permissions != null) {
event.detail(Details.PERMISSION, String.join("|", permissions));
for (String permission : permissions) {
String[] parts = permission.split("#");
String resource = parts[0];
if (parts.length == 1) {
authorizationRequest.addPermission(resource);
} else {
String[] scopes = parts[1].split(",");
authorizationRequest.addPermission(parts[0], scopes);
}
}
}
Metadata metadata = new Metadata();
String responseIncludeResourceName = formParams.getFirst("response_include_resource_name");
if (responseIncludeResourceName != null) {
metadata.setIncludeResourceName(Boolean.parseBoolean(responseIncludeResourceName));
}
String responsePermissionsLimit = formParams.getFirst("response_permissions_limit");
if (responsePermissionsLimit != null) {
metadata.setLimit(Integer.parseInt(responsePermissionsLimit));
}
metadata.setResponseMode(formParams.getFirst("response_mode"));
authorizationRequest.setMetadata(metadata);
Response authorizationResponse = AuthorizationTokenService.instance().authorize(authorizationRequest);
event.success();
return authorizationResponse;
}
use of org.keycloak.jose.jws.JWSInputException 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.jose.jws.JWSInputException in project keycloak by keycloak.
the class OIDCIdentityProvider method extractIdentity.
protected BrokeredIdentityContext extractIdentity(AccessTokenResponse tokenResponse, String accessToken, JsonWebToken idToken) throws IOException {
String id = idToken.getSubject();
BrokeredIdentityContext identity = new BrokeredIdentityContext(id);
String name = (String) idToken.getOtherClaims().get(IDToken.NAME);
String givenName = (String) idToken.getOtherClaims().get(IDToken.GIVEN_NAME);
String familyName = (String) idToken.getOtherClaims().get(IDToken.FAMILY_NAME);
String preferredUsername = (String) idToken.getOtherClaims().get(getusernameClaimNameForIdToken());
String email = (String) idToken.getOtherClaims().get(IDToken.EMAIL);
if (!getConfig().isDisableUserInfoService()) {
String userInfoUrl = getUserInfoUrl();
if (userInfoUrl != null && !userInfoUrl.isEmpty()) {
if (accessToken != null) {
SimpleHttp.Response response = executeRequest(userInfoUrl, SimpleHttp.doGet(userInfoUrl, session).header("Authorization", "Bearer " + accessToken));
String contentType = response.getFirstHeader(HttpHeaders.CONTENT_TYPE);
MediaType contentMediaType;
try {
contentMediaType = MediaType.valueOf(contentType);
} catch (IllegalArgumentException ex) {
contentMediaType = null;
}
if (contentMediaType == null || contentMediaType.isWildcardSubtype() || contentMediaType.isWildcardType()) {
throw new RuntimeException("Unsupported content-type [" + contentType + "] in response from [" + userInfoUrl + "].");
}
JsonNode userInfo;
if (MediaType.APPLICATION_JSON_TYPE.isCompatible(contentMediaType)) {
userInfo = response.asJson();
} else if (APPLICATION_JWT_TYPE.isCompatible(contentMediaType)) {
JWSInput jwsInput;
try {
jwsInput = new JWSInput(response.asString());
} catch (JWSInputException cause) {
throw new RuntimeException("Failed to parse JWT userinfo response", cause);
}
if (verify(jwsInput)) {
userInfo = JsonSerialization.readValue(jwsInput.getContent(), JsonNode.class);
} else {
throw new RuntimeException("Failed to verify signature of userinfo response from [" + userInfoUrl + "].");
}
} else {
throw new RuntimeException("Unsupported content-type [" + contentType + "] in response from [" + userInfoUrl + "].");
}
id = getJsonProperty(userInfo, "sub");
name = getJsonProperty(userInfo, "name");
givenName = getJsonProperty(userInfo, IDToken.GIVEN_NAME);
familyName = getJsonProperty(userInfo, IDToken.FAMILY_NAME);
preferredUsername = getUsernameFromUserInfo(userInfo);
email = getJsonProperty(userInfo, "email");
AbstractJsonUserAttributeMapper.storeUserProfileForMapper(identity, userInfo, getConfig().getAlias());
}
}
}
identity.getContextData().put(VALIDATED_ID_TOKEN, idToken);
identity.setId(id);
if (givenName != null) {
identity.setFirstName(givenName);
}
if (familyName != null) {
identity.setLastName(familyName);
}
if (givenName == null && familyName == null) {
identity.setName(name);
}
identity.setEmail(email);
identity.setBrokerUserId(getConfig().getAlias() + "." + id);
if (preferredUsername == null) {
preferredUsername = email;
}
if (preferredUsername == null) {
preferredUsername = id;
}
identity.setUsername(preferredUsername);
if (tokenResponse != null && tokenResponse.getSessionState() != null) {
identity.setBrokerSessionId(getConfig().getAlias() + "." + tokenResponse.getSessionState());
}
if (tokenResponse != null)
identity.getContextData().put(FEDERATED_ACCESS_TOKEN_RESPONSE, tokenResponse);
if (tokenResponse != null)
processAccessTokenResponse(identity, tokenResponse);
return identity;
}
Aggregations