OAuth-Linking: Don't create new account when claimed identity unknown
Claimed Identity feature was enabled to support old Google OpenID accounts, that cannot be activated anymore. In some corner cases, when for example the URL is not from the production Gerrit site, like it's always the case on staging instance, the OpenID identity may deviate from the original one. In case of mismatch, the lookup for real user for the claimed identity would fail, and the linking mode is lost, and as the consequence a new account is created. Creating new account, when user asked for linking is always the worst option we have, as this cannot be easily undone. Detect this case, preserve the linking mode and keep trying to link instead of create new account. Note that in case this account already exist, the linking would fail with the sane message. Test Plan I: 1. Create gerrit site gerrit.example.org in year 2010 2. Configure the site to use OpenId (non SSO mode) 3. Observe that 85% user base using Google OpeID 4. After Google's OpenID shutdown in May 2015, nobody is able to login anymore using their account and link their identity primary to Launchpad 5. Swap production site to staging site with entire database and Git repository 6. Install gerrit-oauth-provider plugin and activate automagically Google OpenID old token discovery and linking option 7. Configure new OAuth application on Google developer console, but route it to gerrit-test.example.org. Note that this deviation breaks the old OpenID tokens! 8. Test with old user, that has OpenID Google account, that was additionally linked to Launchpad OpenID provider 9. Login with Launchpad OpenID iendtity for this user 10. Profile=>Setting=>Link another identity 11. Select Google OAuth provider offered by the gerrit-oauth-provider plugin 12. Intead of linking to the existing account (or linking error) new account is created 13. This diff fixed this. Error is issued, that the account already exist and linking is not possible Note that when all this would be done on real production site, this error wouldn't happen, because the URL wouldn't deviate and there wouldn't be token mismatch between OpenID token in the database and the token discovered by Google's OAuth OpenID scope. But still, we could easily prevent in this very specific corner case the creation of new account. Note that it's still possible with this setting to create duplicate account for this user by signing in directly with Google OAuth provider without linking mode. However, this would also work as expected on the real production site, because old OpenID token would match with the existing Gerrit account and linking would happen automagically. That is exactly why the option link-to-existing-openid-accounts = true was invented. Unfortunately there is no way to test that this work as expected already on staging Gerrit instance. Test Plan II: 1. User register first time after Google's OpenID 2.0 shut down with OpenID provider, say Launchpad 2. Login with Launchpad 3. Profile=>Setting=>Link another identity 4. Link with Google OAuth2 provider Expected: The OAuth2 identity is linked to the existing account. Actual: New account is created. This diff fixed it. The problem is that I apparently misunderstood the migration spec: [1] and assumed that the OpenID token is provided only when a user was already associated with this site. This is not true. OpenID token also returned for new users, that were never registered with this site before. To rectify it, and still to work for both, known and unknown users, we apply the check. If the OpenID token is known use it for linking. If it is not known, ignore it, but preserve linking mode. * [1] https://developers.google.com/identity/protocols/OpenID2Migration#map-identifiers Change-Id: Icf70cde5fd96cd72aa383218e1d143107a551b45
This commit is contained in:
parent
c9f5a5ab76
commit
e6ac4ad532
@ -125,16 +125,32 @@ class OAuthSessionOverOpenID {
|
||||
try {
|
||||
String claimedIdentifier = user.getClaimedIdentity();
|
||||
Account.Id actualId = accountManager.lookup(user.getExternalId());
|
||||
// Use case 1: claimed identity was provided during handshake phase
|
||||
Account.Id claimedId = null;
|
||||
|
||||
// We try to retrieve claimed identity.
|
||||
// For some reason, for example staging instance
|
||||
// it may deviate from the really old OpenID identity.
|
||||
// What we want to avoid in any event is to create new
|
||||
// account instead of linking to the existing one.
|
||||
// That why we query it here, not to lose linking mode.
|
||||
if (!Strings.isNullOrEmpty(claimedIdentifier)) {
|
||||
Account.Id claimedId = accountManager.lookup(claimedIdentifier);
|
||||
if (claimedId != null && actualId != null) {
|
||||
claimedId = accountManager.lookup(claimedIdentifier);
|
||||
if (claimedId == null) {
|
||||
log.debug("Claimed identity is unknown");
|
||||
}
|
||||
}
|
||||
|
||||
// Use case 1: claimed identity was provided during handshake phase
|
||||
// and user account exists for this identity
|
||||
if (claimedId != null) {
|
||||
log.debug("Claimed identity is set and is known");
|
||||
if (actualId != null) {
|
||||
if (claimedId.equals(actualId)) {
|
||||
// Both link to the same account, that's what we expected.
|
||||
} else {
|
||||
// This is (for now) a fatal error. There are two records
|
||||
// for what might be the same user.
|
||||
//
|
||||
// for what might be the same user. The admin would have to
|
||||
// link the accounts manually.
|
||||
log.error("OAuth accounts disagree over user identity:\n"
|
||||
+ " Claimed ID: " + claimedId + " is " + claimedIdentifier
|
||||
+ "\n" + " Delgate ID: " + actualId + " is "
|
||||
@ -142,7 +158,7 @@ class OAuthSessionOverOpenID {
|
||||
rsp.sendError(HttpServletResponse.SC_FORBIDDEN);
|
||||
return;
|
||||
}
|
||||
} else if (claimedId != null && actualId == null) {
|
||||
} else {
|
||||
// Claimed account already exists: link to it.
|
||||
//
|
||||
try {
|
||||
|
Loading…
Reference in New Issue
Block a user