Skip to content
This repository has been archived by the owner on Nov 3, 2021. It is now read-only.

Commit

Permalink
Merge pull request #24407 from jrburke/bug1069412-email-xoauth2-reauth
Browse files Browse the repository at this point in the history
Bug 1069412 - Email constantly asks me to reauthenticate r=asuth
  • Loading branch information
jrburke committed Sep 27, 2014
2 parents c4657a4 + ca66d86 commit 713382a
Show file tree
Hide file tree
Showing 16 changed files with 344 additions and 138 deletions.
57 changes: 20 additions & 37 deletions apps/email/js/cards/oauth2/fetch.js
Expand Up @@ -2,6 +2,8 @@
define(function(require) {
var queryString = require('query_string');
var services = require('services');
var Cards = require('mail_common').Cards;

// All of the oauthSecrets we know.
var oauthSecrets = services.oauth2;
// The secrets for the oauth we're currently doing.
Expand All @@ -12,18 +14,18 @@ define(function(require) {

var TIMEOUT_MS = 30 * 1000;

var deferred, p, win, winIntervalId, oauthSettings;
var redirectUri = 'http://localhost';

var deferred, p, oauthSettings;

/**
* The postMessage listener called from redirect.html to indicate the final
* data returned from the oauth jump.
* @param {DOMEvent} event the event sent via redirect.html's postMessage.
*/
function oauthOnMessage(event) {
var data = event.data,
origin = document.location.protocol + '//' + document.location.host;

if (data.messageType === 'oauthRedirect' && event.origin === origin) {
function onBrowserComplete(message) {
var data = message.data;
if (message.type === 'oauth2Complete') {
if (!data.code) {
reset('reject', new Error('no code returned'));
return;
Expand Down Expand Up @@ -57,6 +59,10 @@ define(function(require) {
function(err) {
reset('reject', err);
});
} else {
// Treat anything else like a cancel, it failed or was stopped by the
// user.
reset('resolve', { status: 'cancel' });
}
}

Expand Down Expand Up @@ -110,40 +116,19 @@ define(function(require) {
client_secret: curOauthSecrets.clientSecret,
// uh, this doesn't seem right, but the docs want it? hopefully it just
// gets forever ignored?
redirect_uri: 'http://localhost',
redirect_uri: redirectUri,
grant_type: 'authorization_code'
};

xhr.send(queryString.fromObject(redemptionArgs));
});
}

/**
* Checks that the browser window used to show the oauth provider's oauth2
* flow to the user is still in operation. If it is not, it means the user
* canceled the flow by closing that browser window.
*/
function dialogHeartbeat() {
if ((!win || win.closed) && !redeemingCode) {
reset('resolve', { status: 'cancel' });
}
}

function reset(actionName, value) {
console.log('oauth2 fetch reset with action: ' + actionName);
var action = deferred[actionName];
if (winIntervalId) {
clearInterval(winIntervalId);
winIntervalId = 0;
}

if (win && !win.closed) {
win.close();
}

window.removeEventListener('message', oauthOnMessage);

deferred = p = win = null;
deferred = p = null;
redeemingCode = false;

action(value);
Expand Down Expand Up @@ -177,7 +162,7 @@ define(function(require) {
* { accessToken, refreshToken, expireTimeMS }
* * secrets: The clientId and clientSecret used for this oauth.
*/
return function oauthFetch(o2Settings, extraQueryObject) {
return function oauth2Fetch(o2Settings, extraQueryObject) {
// Only one auth session at a time.
if (deferred) {
reset('reject', new Error('Multiple oauth calls, starting new one'));
Expand All @@ -189,8 +174,6 @@ define(function(require) {
};
});

// Set up listening for message from cards/oauth/redirect.html
window.addEventListener('message', oauthOnMessage);
oauthSettings = o2Settings;

curOauthSecrets = undefined;
Expand All @@ -215,7 +198,7 @@ define(function(require) {
// fairly gmail specific with the "installed app" flow, we (must) use
// localhost, but we are maintaining a redirect for our theoretical
// https://email.gaiamobile.org/ domain.
redirect_uri: 'http://localhost',
redirect_uri: redirectUri,
response_type: 'code',
scope: o2Settings.scope,
// max_auth_age set to 0 means that google will not keep the user signed
Expand All @@ -235,10 +218,10 @@ define(function(require) {
}
}

// Need to watch for a window close. Best way is to poll that status of
// the window.
win = window.open(url, '', 'dialog');
winIntervalId = setInterval(dialogHeartbeat, 1000);
Cards.pushCard('setup_oauth2', 'default', 'animate', {
url: url,
onBrowserComplete: onBrowserComplete
});

return p;
};
Expand Down
2 changes: 0 additions & 2 deletions apps/email/js/cards/oauth2/redirect.html
Expand Up @@ -3,8 +3,6 @@
<head>
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1">
<meta charset="utf-8">
<title>OAuth Redirection</title>
<script defer src="redirect.js"></script>
</head>
<body>
</body>
Expand Down
18 changes: 0 additions & 18 deletions apps/email/js/cards/oauth2/redirect.js

This file was deleted.

18 changes: 14 additions & 4 deletions apps/email/js/cards/setup_fix_oauth2.js
Expand Up @@ -36,24 +36,34 @@ SetupFixOAuth2.prototype = {
event.preventDefault();

var oauth2 = this.account._wireRep.credentials.oauth2;
oauthFetch(oauth2)
oauthFetch(oauth2, {
login_hint: this.account.username
})
.then(function(response) {
// Cancellation means hide this UI.
if (response.status === 'cancel') {
this.close();
this.delayedClose();
// Success means victory.
} else if (response.status === 'success') {
this.account.modifyAccount({ oauthTokens: response.tokens });
this.account.clearProblems();
this.close();
this.delayedClose();

// Anything else means a failure and it's also time to close.
} else {
console.error('Unknown oauthFetch status: ' + response.status);
this.close();
this.delayedClose();
}
}.bind(this));
},

delayedClose: function() {
// The setTimeout is a hack. See the comment in setup_progress, in
// onCardVisible, similar issue here, but for the close of the oauth
// card.
setTimeout(this.close.bind(this), 100);
},

close: function(event) {
if (event) {
event.stopPropagation();
Expand Down
10 changes: 10 additions & 0 deletions apps/email/js/cards/setup_oauth2.html
@@ -0,0 +1,10 @@
<section class="card-setup-oauth2 card skin-organic" role="region">
<header class="sup-account-header">
<a href="#" class="sup-back-btn">
<span class="icon icon-back"></span>
</a>
<h1 class="sup-account-header-label header-label"></h1>
</header>
<section class="scrollregion-below-header skin-organic" role="region">
</section>
</section>
102 changes: 102 additions & 0 deletions apps/email/js/cards/setup_oauth2.js
@@ -0,0 +1,102 @@
/*global define*/
'use strict';
define(function(require) {

var templateNode = require('tmpl!./setup_oauth2.html'),
appSelf = require('app_self'),
common = require('mail_common'),
Cards = common.Cards;

function SetupOauth2(domNode, mode, args) {
this.domNode = domNode;
this.onBrowserComplete = args.onBrowserComplete;

this.getElement('sup-back-btn')
.addEventListener('click', this.onBack.bind(this), false);

var browserFrame = document.createElement('iframe');
browserFrame.classList.add('sup-oauth2-browser');
browserFrame.setAttribute('mozbrowser', true);
browserFrame.setAttribute('src', args.url);
browserFrame.addEventListener('mozbrowserlocationchange',
this.onLocationChange.bind(this));

this.getElement('scrollregion-below-header').appendChild(browserFrame);
}

SetupOauth2.prototype = {
die: function() {
// The main reason a mozbrowser for this flow, not preserving cookies.
appSelf.latest('self', function(app) {
console.log('clearing browser data: ' + app);
// Each app has two cookie jars, one for the app and one for browsing
// contexts. This call just clears the browsing context.
if (app) {
app.clearBrowserData();
}
console.log('browser data cleared');
});
},

getElement: function(className) {
return this.domNode.getElementsByClassName(className)[0];
},

close: function() {
Cards.removeCardAndSuccessors(this.domNode, 'animate', 1);
},

onBack: function(event) {
if (event) {
event.stopPropagation();
event.preventDefault();
}

this.onBrowserComplete({
type: 'oauth2Cancel'
});

this.close();
},

onLocationChange: function(event) {
var url = event.detail;

// If the URL is not an expected app one for the redirect page
// (set up in the manifest.webapp), then just update the title.
if (url.indexOf('app:') !== 0 ||
url.indexOf('cards/oauth2/redirect.html?') === -1) {
this.getElement('sup-account-header-label').textContent = url;
return;
}

//Start closing the dialog now to avoid the user staring too long at an
//empty white screen.
this.close();

// Should have the data we need now.
var search = url.split('?')[1] || '';
var result = {
type: 'oauth2Complete',
data: {}
};

var elements = search.split('&');
elements.forEach(function(p) {
var values = p.split('=');
result.data[decodeURIComponent(values[0])] =
decodeURIComponent(values[1]);
});

this.onBrowserComplete(result);
}
};
Cards.defineCardWithDefaultMode(
'setup_oauth2',
{ tray: false },
SetupOauth2,
templateNode
);

return SetupOauth2;
});

0 comments on commit 713382a

Please sign in to comment.