Skip to content
This repository was archived by the owner on Nov 25, 2020. It is now read-only.

Commit

Permalink
refactor(authService): consistent redirect, oauth moved to authentica…
Browse files Browse the repository at this point in the history
…tion

BREAKING CHANGE: for AuthService(provider, redirectUri, userData) redirectUri===false means "Do not redirect" now. Set redirectUrl to undefined or null to use the defaultRedirectUrl.(which is in this case  BaseConfig.loginRedirect)
DEPRECATED: for AuthService(provider, redirectUri, userData) redirectUri === true to actually not redirect is deprecated. Set redirectUrl===false instead.
  • Loading branch information
doktordirk committed Apr 10, 2016
1 parent 194a74d commit 2c15244
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 89 deletions.
70 changes: 37 additions & 33 deletions src/authService.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,11 @@ import {inject} from 'aurelia-dependency-injection';

import {Authentication} from './authentication';
import {BaseConfig} from './baseConfig';
import {OAuth1} from './oAuth1';
import {OAuth2} from './oAuth2';

@inject(Authentication, OAuth1, OAuth2, BaseConfig)
@inject(Authentication, BaseConfig)
export class AuthService {
constructor(authentication, oAuth1, oAuth2, config) {
constructor(authentication, config) {
this.authentication = authentication;
this.oAuth1 = oAuth1;
this.oAuth2 = oAuth2;
this.config = config;
}

Expand Down Expand Up @@ -156,14 +152,14 @@ export class AuthService {
return this._signup(content);
}

_signup(data) {
_signup(data, redirectUri) {
return this.client.post(this.config.withBase(this.config.signupUrl), data)
.then(response => {
if (this.config.loginOnSignup) {
this.authentication.setAccessTokenFromResponse(response);
} else if (this.config.signupRedirect) {
window.location.href = this.config.signupRedirect;
this.authentication.setTokensFromResponse(response);
}
this.authentication.redirect(redirectUri, this.config.signupRedirect);

return response;
});
}
Expand All @@ -189,17 +185,16 @@ export class AuthService {
return this._login(content);
}

_login(data) {
_login(data, redirectUri) {
if (this.config.clientId) {
data.client_id = this.config.clientId;
}

return this.client.post(this.config.withBase(this.config.loginUrl), data)
.then(response => {
this.authentication.setAccessTokenFromResponse(response);
if (this.config.useRefreshToken) {
this.authentication.setRefreshTokenFromResponse(response);
}
this.authentication.setTokensFromResponse(response);

this.authentication.redirect(redirectUri, this.config.loginRedirect);

return response;
});
Expand All @@ -214,7 +209,12 @@ export class AuthService {
*
*/
logout(redirectUri) {
return this.authentication.logout(redirectUri);
return this.authentication.logout(redirectUri)
.then(response => {
this.authentication.redirect(redirectUri, this.config.logoutRedirect);

return response;
});
}

/**
Expand All @@ -224,9 +224,9 @@ export class AuthService {
*
*/
updateToken() {
this.isRefreshing = true;
const refreshToken = this.authentication.refreshToken;
let content = {};
this.isRefreshing = true;
const refreshToken = this.authentication.refreshToken;
let content = {};

if (refreshToken) {
content = {grant_type: 'refresh_token', refresh_token: refreshToken};
Expand All @@ -236,17 +236,15 @@ export class AuthService {

return this.client.post(this.config.withBase(this.config.loginUrl), content)
.then(response => {
this.authentication.setRefreshTokenFromResponse(response);
this.authentication.setAccessTokenFromResponse(response);
this.isRefreshing = false;

this.authentication.setTokensFromResponse(response);
return response;
}).catch(err => {
this.authentication.accessToken = null;
this.authentication.refreshToken = null;
this.isRefreshing = false;

this.authentication.removeTokens();
throw err;
})
.then(response => {
this.isRefreshing = false;
return response;
});
}

Expand All @@ -264,11 +262,12 @@ export class AuthService {
*
*/
authenticate(name, redirectUri, userData = {}) {
const provider = this.config.providers[name].type === '1.0' ? this.oAuth1 : this.oAuth2;

return provider.open(this.config.providers[name], userData)
return this.authentication.authenticate(name, userData)
.then(response => {
this.authentication.setAccessTokenFromResponse(response, redirectUri);
this.authentication.setTokensFromResponse(response);

this.authentication.redirect(redirectUri, this.config.loginRedirect);

return response;
});
}
Expand All @@ -281,8 +280,13 @@ export class AuthService {
* @return {Promise<response>}
*
*/
unlink(name) {
unlink(name, redirectUri) {
const unlinkUrl = this.config.withBase(this.config.unlinkUrl) + name;
return this.client.request(this.config.unlinkMethod, unlinkUrl);
return this.client.request(this.config.unlinkMethod, unlinkUrl)
.then(response => {
this.authentication.redirect(redirectUri);

return response;
});
}
}
73 changes: 55 additions & 18 deletions src/authentication.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@ import {inject} from 'aurelia-dependency-injection';

import {BaseConfig} from './baseConfig';
import {Storage} from './storage';
import {OAuth1} from './oAuth1';
import {OAuth2} from './oAuth2';

@inject(Storage, BaseConfig)
@inject(Storage, BaseConfig, OAuth1, OAuth2)
export class Authentication {
constructor(storage, config) {
constructor(storage, config, oAuth1, oAuth2) {
this.storage = storage;
this.config = config;
this.oAuth1 = oAuth1;
this.oAuth2 = oAuth2;
}

getLoginRoute() {
Expand Down Expand Up @@ -128,19 +132,13 @@ export class Authentication {
return response[tokenName] === undefined ? null : response[tokenName];
}

setAccessTokenFromResponse(response, redirect) {
setAccessTokenFromResponse(response) {
const config = this.config;
const newToken = this.getTokenFromResponse(response, config.accessTokenProp, config.accessTokenName, config.accessTokenRoot);

if (!newToken) throw new Error('Token not found in response');

this.accessToken = newToken;

if (this.config.loginRedirect && !redirect) {
window.location.href = this.config.loginRedirect;
} else if (typeof redirect === 'string') {
window.location.href = window.encodeURI(redirect);
}
}

setRefreshTokenFromResponse(response) {
Expand All @@ -152,18 +150,57 @@ export class Authentication {
this.refreshToken = newToken;
}

logout(redirect) {
return new Promise(resolve => {
this.accessToken = null;
this.refreshToken = null;
setTokensFromResponse(response) {
this.setAccessTokenFromResponse(response);

if (this.config.logoutRedirect && !redirect) {
window.location.href = this.config.logoutRedirect;
} else if (typeof redirect === 'string') {
window.location.href = redirect;
}
if (this.config.useRefreshToken) {
this.setRefreshTokenFromResponse(response);
}
}

removeTokens() {
this.accessToken = null;
this.refreshToken = null;
}

logout() {
return new Promise(resolve => {
this.removeTokens();

resolve();
});
}

/**
* Authenticate with third-party
*
* @param {String} name of the provider
* @param {[{}]} [userData]
*
* @return {Promise<response>}
*
*/
authenticate(name, userData = {}) {
const provider = this.config.providers[name].type === '1.0' ? this.oAuth1 : this.oAuth2;

return provider.open(this.config.providers[name], userData);
}

redirect(redirectUrl, defaultRedirectUrl) {
// stupid rule to keep it BC
if (redirectUrl === true) {
console.warn('Setting redirectUrl === true to actually not redirect is deprecated. Set redirectUrl===false instead.');
return;
}
// explicit false means don't redirect
if (redirectUrl === false) {
console.warn('Setting redirectUrl === false to actually use the defaultRedirectUrl has changed. It means "Do not redirect" now. Set redirectUrl to undefined or null to use the defaultRedirectUrl.');
return;
}
if (typeof redirectUrl === 'string') {
window.location.href = window.encodeURI(redirectUrl);
} else if (defaultRedirectUrl) {
window.location.href = defaultRedirectUrl;
}
}
}
59 changes: 21 additions & 38 deletions test/authService.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ describe('AuthService', () => {
});

it('Should unlink provider using POST.', (done) => {
const container = getContainer();
const container = getContainer();
const authService = container.get(AuthService);
authService.config.unlinkMethod = 'post';

Expand All @@ -254,39 +254,30 @@ describe('AuthService', () => {
});

describe('.authenticate()', () => {
let oAuth1;
let oAuth2;

beforeEach(() => {
oAuth1 = {
open: (provider, userData) => Promise.resolve({
provider: provider,
userData: userData,
access_token: 'oauth1'
})
};
oAuth2 = {
open: (provider, userData) => Promise.resolve({
provider: provider,
userData: userData,
access_token: 'oauth2'
})
};
const container = getContainer();
const authentication = container.get(Authentication);
const baseConfig = container.get(BaseConfig);

authentication.oAuth1.open = (provider, userData) => Promise.resolve({
provider: provider,
userData: userData,
access_token: 'oauth1'
});

authentication.oAuth2.open = (provider, userData) => Promise.resolve({
provider: provider,
userData: userData,
access_token: 'oauth2'
});

afterEach((done) => {
const container = getContainer();
const authService = container.get(AuthService);
authService.logout().then(done);
});

it('Should authenticate with oAuth1 provider and login.', (done) => {
const container = getContainer();
const baseConfig = container.get(BaseConfig);
const authentication = container.get(Authentication);

const authService = new AuthService(authentication, oAuth1, oAuth2, baseConfig);
spyOn(oAuth1, 'open').and.callThrough();
const authService = new AuthService(authentication, baseConfig);
spyOn(authentication.oAuth1, 'open').and.callThrough();

authService.authenticate('twitter', null, {data: 'some'})
.then(response => {
Expand All @@ -308,12 +299,8 @@ describe('AuthService', () => {
});

it('Should authenticate with oAuth2 provider and login.', (done) => {
const container = getContainer();
const baseConfig = container.get(BaseConfig);
const authentication = container.get(Authentication);

const authService = new AuthService(authentication, oAuth1, oAuth2, baseConfig);
spyOn(oAuth2, 'open').and.callThrough();
const authService = new AuthService(authentication, baseConfig);
spyOn(authentication.oAuth2, 'open').and.callThrough();

authService.authenticate('facebook', null, {data: 'some'})
.then(response => {
Expand All @@ -335,12 +322,8 @@ describe('AuthService', () => {
});

it('Should try to authenticate with and fail.', (done) => {
const container = getContainer();
const baseConfig = container.get(BaseConfig);
const authentication = container.get(Authentication);

const authService = new AuthService(authentication, oAuth1, oAuth2, baseConfig);
spyOn(oAuth2, 'open').and.returnValue(Promise.resolve());
const authService = new AuthService(authentication, baseConfig);
spyOn(authentication.oAuth2, 'open').and.returnValue(Promise.resolve());

authService.authenticate('facebook')
.then(res => {
Expand Down
12 changes: 12 additions & 0 deletions test/authentication.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -276,4 +276,16 @@ describe('Authentication', () => {
});
});
});

describe('.redirect', () => {
const container = new Container();
const authentication = container.get(Authentication);

it('should not redirect with redirectUri===false', () => {
authentication.redirect(false, 'somewhere');

// basically just don't get the window reload error
expect(true).toBe(true);
});
});
});

0 comments on commit 2c15244

Please sign in to comment.