Skip to content

Commit

Permalink
Integrating Google One Tap
Browse files Browse the repository at this point in the history
  • Loading branch information
indeewari committed Jul 28, 2022
1 parent 0c6ec14 commit 56cfbac
Show file tree
Hide file tree
Showing 3 changed files with 205 additions and 67 deletions.
86 changes: 86 additions & 0 deletions GOT_OIDC.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
Index: components/org.wso2.carbon.identity.application.authenticator.oidc/src/main/java/org/wso2/carbon/identity/application/authenticator/oidc/OpenIDConnectAuthenticator.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/components/org.wso2.carbon.identity.application.authenticator.oidc/src/main/java/org/wso2/carbon/identity/application/authenticator/oidc/OpenIDConnectAuthenticator.java b/components/org.wso2.carbon.identity.application.authenticator.oidc/src/main/java/org/wso2/carbon/identity/application/authenticator/oidc/OpenIDConnectAuthenticator.java
--- a/components/org.wso2.carbon.identity.application.authenticator.oidc/src/main/java/org/wso2/carbon/identity/application/authenticator/oidc/OpenIDConnectAuthenticator.java (revision 5844c8078e123699e34446eeb362864714c89983)
+++ b/components/org.wso2.carbon.identity.application.authenticator.oidc/src/main/java/org/wso2/carbon/identity/application/authenticator/oidc/OpenIDConnectAuthenticator.java (date 1657176530471)
@@ -452,24 +452,12 @@
protected void processAuthenticationResponse(HttpServletRequest request, HttpServletResponse response,
AuthenticationContext context) throws AuthenticationFailedException {

- try {
-
- OAuthAuthzResponse authzResponse = OAuthAuthzResponse.oauthCodeAuthzResponse(request);
- OAuthClientRequest accessTokenRequest = getAccessTokenRequest(context, authzResponse);
-
- // Create OAuth client that uses custom http client under the hood
- OAuthClient oAuthClient = new OAuthClient(new URLConnectionClient());
- OAuthClientResponse oAuthResponse = getOauthResponse(oAuthClient, accessTokenRequest);
+ OAuthClientResponse oAuthResponse = generateOauthResponse(request, context);

// TODO : return access token and id token to framework
- String accessToken = oAuthResponse.getParam(OIDCAuthenticatorConstants.ACCESS_TOKEN);
+ mapAccessToken(context, oAuthResponse);
+ String idToken = mapIdToken(request, oAuthResponse);

- if (StringUtils.isBlank(accessToken)) {
- throw new AuthenticationFailedException(ErrorMessages.ACCESS_TOKEN_EMPTY_OR_NULL.getCode(),
- ErrorMessages.ACCESS_TOKEN_EMPTY_OR_NULL.getMessage());
- }
-
- String idToken = oAuthResponse.getParam(OIDCAuthenticatorConstants.ID_TOKEN);
Map<String, String> authenticatorProperties = context.getAuthenticatorProperties();
if (StringUtils.isBlank(idToken) && requiredIDToken(authenticatorProperties)) {
throw new AuthenticationFailedException(ErrorMessages.ID_TOKEN_MISSED_IN_OIDC_RESPONSE.getCode(),
@@ -482,8 +470,6 @@
stateInfoOIDC.setIdTokenHint(idToken);
context.setStateInfo(stateInfoOIDC);

- context.setProperty(OIDCAuthenticatorConstants.ACCESS_TOKEN, accessToken);
-
AuthenticatedUser authenticatedUser;
Map<ClaimMapping, String> claims = new HashMap<>();
Map<String, Object> jsonObject = new HashMap<>();
@@ -535,10 +521,38 @@

context.setSubject(authenticatedUser);

+ }
+
+ protected String mapIdToken(HttpServletRequest request, OAuthClientResponse oAuthResponse) {
+ String idToken = oAuthResponse.getParam(OIDCAuthenticatorConstants.ID_TOKEN);
+ return idToken;
+ }
+
+ protected void mapAccessToken(AuthenticationContext context, OAuthClientResponse oAuthResponse) throws AuthenticationFailedException {
+ String accessToken = oAuthResponse.getParam(OIDCAuthenticatorConstants.ACCESS_TOKEN);
+
+ if (StringUtils.isBlank(accessToken)) {
+ throw new AuthenticationFailedException(ErrorMessages.ACCESS_TOKEN_EMPTY_OR_NULL.getCode(),
+ ErrorMessages.ACCESS_TOKEN_EMPTY_OR_NULL.getMessage());
+ }
+ context.setProperty(OIDCAuthenticatorConstants.ACCESS_TOKEN, accessToken);
+ }
+
+ protected OAuthClientResponse generateOauthResponse(HttpServletRequest request, AuthenticationContext context)
+ throws AuthenticationFailedException {
+
+ OAuthClientResponse oAuthResponse;
+ try {
+ OAuthAuthzResponse authzResponse = OAuthAuthzResponse.oauthCodeAuthzResponse(request);
+ OAuthClientRequest accessTokenRequest = getAccessTokenRequest(context, authzResponse);
+
+ // Create OAuth client that uses custom http client under the hood
+ OAuthClient oAuthClient = new OAuthClient(new URLConnectionClient());
+ oAuthResponse = getOauthResponse(oAuthClient,accessTokenRequest);
} catch (OAuthProblemException e) {
- throw new AuthenticationFailedException(ErrorMessages.AUTHENTICATION_PROCESS_FAILED.getCode(),
- ErrorMessages.AUTHENTICATION_PROCESS_FAILED.getMessage(), context.getSubject(), e);
+ throw new RuntimeException(e);
}
+ return oAuthResponse;
}

@Override
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ private OIDCAuthenticatorConstants() {
public static final String OAUTH2_AUTHZ_URL = "OAuth2AuthzEPUrl";
public static final String OAUTH2_TOKEN_URL = "OAuth2TokenEPUrl";
public static final String IS_BASIC_AUTH_ENABLED = "IsBasicAuthEnabled";

public static final String OIDC_QUERY_PARAM_MAP_PROPERTY_KEY = "oidc:param.map";

public static final String HTTP_ORIGIN_HEADER = "Origin";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,16 @@ public boolean canHandle(HttpServletRequest request) {
return false;
}

private boolean isInitialRequest(AuthenticationContext context, HttpServletRequest request) {
/**
* There are several types of requests such as authorization request, token request as well as different stages
* like logout, error etc.
* This method identifies if the request is an initial request or not, so that will help to initialize the request.
*
* @param context AuthenticationContext.
* @param request HttpServletRequest.
* @return Whether this is an initial request or not.
*/
protected boolean isInitialRequest(AuthenticationContext context, HttpServletRequest request) {

return !context.isLogoutRequest() && !hasCodeParamInRequest(request) && !hasErrorParamInRequest(request);
}
Expand Down Expand Up @@ -452,95 +461,139 @@ private String getOIDCAuthzEndpoint(Map<String, String> authenticatorProperties)
protected void processAuthenticationResponse(HttpServletRequest request, HttpServletResponse response,
AuthenticationContext context) throws AuthenticationFailedException {

try {
/*
* oAuthResponse can be null in some authentication flows. i.e Google One Tap.
*/
OAuthClientResponse oAuthResponse = generateOauthResponse(request, context);
// TODO : return access token and id token to framework
mapAccessToken(request, context, oAuthResponse);
String idToken = mapIdToken(context, request, oAuthResponse);

OAuthAuthzResponse authzResponse = OAuthAuthzResponse.oauthCodeAuthzResponse(request);
OAuthClientRequest accessTokenRequest = getAccessTokenRequest(context, authzResponse);
Map<String, String> authenticatorProperties = context.getAuthenticatorProperties();
if (StringUtils.isBlank(idToken) && requiredIDToken(authenticatorProperties)) {
throw new AuthenticationFailedException(ErrorMessages.ID_TOKEN_MISSED_IN_OIDC_RESPONSE.getCode(),
String.format(ErrorMessages.ID_TOKEN_MISSED_IN_OIDC_RESPONSE.getMessage(),
getTokenEndpoint(authenticatorProperties),
authenticatorProperties.get(OIDCAuthenticatorConstants.CLIENT_ID)));
}

// Create OAuth client that uses custom http client under the hood
OAuthClient oAuthClient = new OAuthClient(new URLConnectionClient());
OAuthClientResponse oAuthResponse = getOauthResponse(oAuthClient, accessTokenRequest);
OIDCStateInfo stateInfoOIDC = new OIDCStateInfo();
stateInfoOIDC.setIdTokenHint(idToken);
context.setStateInfo(stateInfoOIDC);

// TODO : return access token and id token to framework
String accessToken = oAuthResponse.getParam(OIDCAuthenticatorConstants.ACCESS_TOKEN);
AuthenticatedUser authenticatedUser;
Map<ClaimMapping, String> claims = new HashMap<>();
Map<String, Object> jsonObject = new HashMap<>();

if (StringUtils.isBlank(accessToken)) {
throw new AuthenticationFailedException(ErrorMessages.ACCESS_TOKEN_EMPTY_OR_NULL.getCode(),
ErrorMessages.ACCESS_TOKEN_EMPTY_OR_NULL.getMessage());
if (StringUtils.isNotBlank(idToken)) {
jsonObject = getIdTokenClaims(context, idToken);
if (jsonObject.isEmpty()) {
String errorMessage = ErrorMessages.DECODED_JSON_OBJECT_IS_NULL.getMessage();
if (log.isDebugEnabled()) {
log.debug(errorMessage);
}
throw new AuthenticationFailedException(ErrorMessages.DECODED_JSON_OBJECT_IS_NULL.getCode(),
errorMessage);
}

String idToken = oAuthResponse.getParam(OIDCAuthenticatorConstants.ID_TOKEN);
Map<String, String> authenticatorProperties = context.getAuthenticatorProperties();
if (StringUtils.isBlank(idToken) && requiredIDToken(authenticatorProperties)) {
throw new AuthenticationFailedException(ErrorMessages.ID_TOKEN_MISSED_IN_OIDC_RESPONSE.getCode(),
String.format(ErrorMessages.ID_TOKEN_MISSED_IN_OIDC_RESPONSE.getMessage(),
getTokenEndpoint(authenticatorProperties),
authenticatorProperties.get(OIDCAuthenticatorConstants.CLIENT_ID)));
String idpName = context.getExternalIdP().getIdPName();
String sidClaim = (String) jsonObject.get(OIDCAuthenticatorConstants.Claim.SID);
if (StringUtils.isNotBlank(sidClaim) && StringUtils.isNotBlank(idpName)) {
// Add 'sid' claim into authentication context, to be stored in the UserSessionStore for single logout.
context.setProperty(FEDERATED_IDP_SESSION_ID + idpName, sidClaim);
}

OIDCStateInfo stateInfoOIDC = new OIDCStateInfo();
stateInfoOIDC.setIdTokenHint(idToken);
context.setStateInfo(stateInfoOIDC);

context.setProperty(OIDCAuthenticatorConstants.ACCESS_TOKEN, accessToken);
if (log.isDebugEnabled() && IdentityUtil
.isTokenLoggable(IdentityConstants.IdentityTokens.USER_ID_TOKEN)) {
log.debug("Retrieved the User Information:" + jsonObject);
}

processAuthenticatedUserScopes(context, oAuthResponse.getParam(OAuthConstants.OAuth20Params.SCOPE));
String authenticatedUserId = getAuthenticatedUserId(context, oAuthResponse, jsonObject);
String attributeSeparator = getMultiAttributeSeparator(context, authenticatedUserId);

AuthenticatedUser authenticatedUser;
Map<ClaimMapping, String> claims = new HashMap<>();
Map<String, Object> jsonObject = new HashMap<>();
jsonObject.entrySet().stream()
.filter(entry -> !ArrayUtils.contains(NON_USER_ATTRIBUTES, entry.getKey()))
.forEach(entry -> buildClaimMappings(claims, entry, attributeSeparator));

if (StringUtils.isNotBlank(idToken)) {
jsonObject = getIdTokenClaims(context, idToken);
if (jsonObject == null) {
String errorMessage = ErrorMessages.DECODED_JSON_OBJECT_IS_NULL.getMessage();
if (log.isDebugEnabled()) {
log.debug(errorMessage);
}
throw new AuthenticationFailedException(ErrorMessages.DECODED_JSON_OBJECT_IS_NULL.getCode(),
errorMessage);
}
authenticatedUser = AuthenticatedUser
.createFederateAuthenticatedUserFromSubjectIdentifier(authenticatedUserId);
} else {
if (log.isDebugEnabled()) {
log.debug("The IdToken is null");
}
authenticatedUser = AuthenticatedUser.createFederateAuthenticatedUserFromSubjectIdentifier(
getAuthenticateUser(context, jsonObject, oAuthResponse));
}
claims.putAll(getSubjectAttributes(oAuthResponse, authenticatorProperties));
authenticatedUser.setUserAttributes(claims);
context.setSubject(authenticatedUser);
}

String idpName = context.getExternalIdP().getIdPName();
String sidClaim = (String) jsonObject.get(OIDCAuthenticatorConstants.Claim.SID);
if (StringUtils.isNotBlank(sidClaim) && StringUtils.isNotBlank(idpName)) {
// Add 'sid' claim into authentication context, to be stored in the UserSessionStore for
// single logout.
context.setProperty(FEDERATED_IDP_SESSION_ID + idpName, sidClaim);
}
/**
* Retrieves or maps the ID token according to the flow supported by the authenticator.
*
* @param context AuthenticationContext.
* @param request HttpServletRequest.
* @param oAuthResponse OAuthClientResponse.
* @return The ID token.
*/
protected String mapIdToken(AuthenticationContext context, HttpServletRequest request,
OAuthClientResponse oAuthResponse) {

if (log.isDebugEnabled() && IdentityUtil
.isTokenLoggable(IdentityConstants.IdentityTokens.USER_ID_TOKEN)) {
log.debug("Retrieved the User Information:" + jsonObject);
}
return oAuthResponse.getParam(OIDCAuthenticatorConstants.ID_TOKEN);
}

String authenticatedUserId = getAuthenticatedUserId(context, oAuthResponse, jsonObject);
String attributeSeparator = getMultiAttributeSeparator(context, authenticatedUserId);
/**
* Retrieves or maps the access token according to the flow supported by the authenticator.
*
* @param request HttpServletRequest.
* @param context AuthenticationContext.
* @param oAuthResponse OAuthClientResponse.
* @throws AuthenticationFailedException Throws error when access token is not found.
*/
protected void mapAccessToken(HttpServletRequest request, AuthenticationContext context,
OAuthClientResponse oAuthResponse) throws AuthenticationFailedException {

jsonObject.entrySet().stream()
.filter(entry -> !ArrayUtils.contains(NON_USER_ATTRIBUTES, entry.getKey()))
.forEach(entry -> buildClaimMappings(claims, entry, attributeSeparator));

authenticatedUser = AuthenticatedUser
.createFederateAuthenticatedUserFromSubjectIdentifier(authenticatedUserId);
} else {
String accessToken = oAuthResponse.getParam(OIDCAuthenticatorConstants.ACCESS_TOKEN);

if (log.isDebugEnabled()) {
log.debug("The IdToken is null");
}
authenticatedUser = AuthenticatedUser.createFederateAuthenticatedUserFromSubjectIdentifier(
getAuthenticateUser(context, jsonObject, oAuthResponse));
if (StringUtils.isBlank(accessToken)) {
if (log.isDebugEnabled()) {
log.debug("No access token found");
}
throw new AuthenticationFailedException(ErrorMessages.ACCESS_TOKEN_EMPTY_OR_NULL.getCode(),
ErrorMessages.ACCESS_TOKEN_EMPTY_OR_NULL.getMessage());
}
context.setProperty(OIDCAuthenticatorConstants.ACCESS_TOKEN, accessToken);
}

claims.putAll(getSubjectAttributes(oAuthResponse, authenticatorProperties));
authenticatedUser.setUserAttributes(claims);
/**
* Generates OAuth client and returns the oAuthResponse according to the flow supported by the authenticator.
*
* @param request HttpServletRequest.
* @param context AuthenticationContext.
* @return OAuthClientResponse.
* @throws AuthenticationFailedException throws error when OAuthAuthzResponse validation fails for either error
* response or the parameters.
*/
protected OAuthClientResponse generateOauthResponse(HttpServletRequest request, AuthenticationContext context)
throws AuthenticationFailedException {

context.setSubject(authenticatedUser);
OAuthClientResponse oAuthResponse;
try {
OAuthAuthzResponse authzResponse = OAuthAuthzResponse.oauthCodeAuthzResponse(request);
OAuthClientRequest accessTokenRequest = getAccessTokenRequest(context, authzResponse);

// Create OAuth client that uses custom http client under the hood.
OAuthClient oAuthClient = new OAuthClient(new URLConnectionClient());
oAuthResponse = getOauthResponse(oAuthClient, accessTokenRequest);
if (oAuthResponse != null) {
processAuthenticatedUserScopes(context, oAuthResponse.getParam(OAuthConstants.OAuth20Params.SCOPE));
}
} catch (OAuthProblemException e) {
throw new AuthenticationFailedException(ErrorMessages.AUTHENTICATION_PROCESS_FAILED.getCode(),
ErrorMessages.AUTHENTICATION_PROCESS_FAILED.getMessage(), context.getSubject(), e);
}
return oAuthResponse;
}

protected void processAuthenticatedUserScopes(AuthenticationContext context, String scopes) {
Expand Down

0 comments on commit 56cfbac

Please sign in to comment.