Commit 750c666b authored by Robert Knight's avatar Robert Knight

Revoke tokens using /oauth/revoke endpoint when logging out.

Use the service's OAuth token revocation endpoint [1] to revoke access
and refresh tokens when the user logs out.

[1] https://github.com/hypothesis/h/pull/4620
parent 1a2c4aac
......@@ -81,14 +81,12 @@ function auth($http, $window, flash, localStorage, random, settings) {
};
}
// Post the given data to the tokenUrl endpoint as a form submission.
// Return a Promise for the access token response.
function postToTokenUrl(data) {
function formPost(url, data) {
data = queryString.stringify(data);
var requestConfig = {
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
};
return $http.post(tokenUrl, data, requestConfig);
return $http.post(url, data, requestConfig);
}
function grantTokenFromHostPage() {
......@@ -151,7 +149,7 @@ function auth($http, $window, flash, localStorage, random, settings) {
grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',
assertion: grantToken,
};
return postToTokenUrl(data).then(function (response) {
return formPost(tokenUrl, data).then(function (response) {
if (response.status !== 200) {
throw new Error('Failed to retrieve access token');
}
......@@ -169,7 +167,7 @@ function auth($http, $window, flash, localStorage, random, settings) {
grant_type: 'authorization_code',
code,
};
return postToTokenUrl(data).then((response) => {
return formPost(tokenUrl, data).then((response) => {
if (response.status !== 200) {
throw new Error('Authorization code exchange failed');
}
......@@ -187,7 +185,7 @@ function auth($http, $window, flash, localStorage, random, settings) {
*/
function refreshAccessToken(refreshToken, options) {
var data = { grant_type: 'refresh_token', refresh_token: refreshToken };
return postToTokenUrl(data).then((response) => {
return formPost(tokenUrl, data).then((response) => {
var tokenInfo = tokenInfoFrom(response);
if (options.persist) {
......@@ -369,8 +367,13 @@ function auth($http, $window, flash, localStorage, random, settings) {
* use it.
*/
function logout() {
return accessTokenPromise.then(accessToken => {
return formPost(settings.oauthRevokeUrl, {
token: accessToken,
});
}).then(() => {
clearCache();
return Promise.resolve(null);
});
}
return {
......
......@@ -109,6 +109,7 @@ describe('sidebar.oauth-auth', function () {
apiUrl: 'https://hypothes.is/api/',
oauthAuthorizeUrl: 'https://hypothes.is/oauth/authorize/',
oauthClientId: 'the-client-id',
oauthRevokeUrl: 'https://hypothes.is/oauth/revoke/',
services: [{
authority: 'publisher.org',
grantToken: 'a.jwt.token',
......@@ -564,6 +565,8 @@ describe('sidebar.oauth-auth', function () {
return auth.tokenGetter();
}).then(token => {
assert.notEqual(token, null);
fakeHttp.post.reset();
});
});
......@@ -580,6 +583,15 @@ describe('sidebar.oauth-auth', function () {
assert.calledWith(fakeLocalStorage.removeItem, TOKEN_KEY);
});
});
it('revokes tokens', () => {
return auth.logout().then(() => {
var expectedBody = 'token=firstAccessToken';
assert.calledWith(fakeHttp.post, 'https://hypothes.is/oauth/revoke/', expectedBody, {
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
});
});
});
});
// Advance time forward so that any current access tokens will have expired.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment