Skip to content

Commit

Permalink
feat(auth): add a back link to the strategy name in the token (#571)
Browse files Browse the repository at this point in the history
The token now contains `ownerStrategyName` field, which is a backlink to the strategy used to create the token.

BREAKING CHANGE:
`nbAuthCreateToken` (token.ts) function now takes a third parameter, which is the `ownerStrategyName`.
Since `nbAuthCreateToken` is a part of public API this could *potentially* introduce a breaking change.
  • Loading branch information
alain-charles authored and nnixaa committed Jul 23, 2018
1 parent 9cbaa45 commit 1c89636
Show file tree
Hide file tree
Showing 11 changed files with 111 additions and 63 deletions.
6 changes: 4 additions & 2 deletions src/framework/auth/services/auth.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,14 @@ describe('auth-service', () => {
let tokenService: NbTokenService;
let dummyAuthStrategy: NbDummyAuthStrategy;
const testTokenValue = 'test-token';
const ownerStrategyName = 'strategy';


const resp401 = new HttpResponse<Object>({body: {}, status: 401});
const resp200 = new HttpResponse<Object>({body: {}, status: 200});

const testToken = nbAuthCreateToken(NbAuthSimpleToken, testTokenValue);
const emptyToken = nbAuthCreateToken(NbAuthSimpleToken, null);
const testToken = nbAuthCreateToken(NbAuthSimpleToken, testTokenValue, ownerStrategyName);
const emptyToken = nbAuthCreateToken(NbAuthSimpleToken, null, ownerStrategyName);

const failResult = new NbAuthResult(false,
resp401,
Expand Down
14 changes: 8 additions & 6 deletions src/framework/auth/services/token/token-parceler.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@ import { NB_AUTH_TOKENS } from '../../auth.options';
describe('token-parceler', () => {

let tokenParceler: NbAuthTokenParceler;
const simpleToken = nbAuthCreateToken(NbAuthSimpleToken, 'test value');
const wrappedSimple = `{"name":"${NbAuthSimpleToken.NAME}","value":"${simpleToken.getValue()}"}`;
const simpleToken = nbAuthCreateToken(NbAuthSimpleToken, 'test value', 'strategy');
// tslint:disable-next-line
const jwtToken = nbAuthCreateToken(NbAuthJWTToken, 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzY290Y2guaW8iLCJleHAiOjI1MTczMTQwNjYxNzUsIm5hbWUiOiJDaHJpcyBTZXZpbGxlamEiLCJhZG1pbiI6dHJ1ZX0=.03f329983b86f7d9a9f5fef85305880101d5e302afafa20154d094b229f75773');
const wrappedJWT = `{"name":"${NbAuthJWTToken.NAME}","value":"${jwtToken.getValue()}"}`;

const wrappedNonExisting = `{"name":"non-existing","value":"${simpleToken.getValue()}"}`;
const wrappedSimple = `{"name":"${NbAuthSimpleToken.NAME}","ownerStrategyName":"${simpleToken.getOwnerStrategyName()}","value":"${simpleToken.getValue()}"}`;
// tslint:disable-next-line
const jwtToken = nbAuthCreateToken(NbAuthJWTToken, 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzY290Y2guaW8iLCJleHAiOjI1MTczMTQwNjYxNzUsIm5hbWUiOiJDaHJpcyBTZXZpbGxlamEiLCJhZG1pbiI6dHJ1ZX0=.03f329983b86f7d9a9f5fef85305880101d5e302afafa20154d094b229f75773', 'strategy');
// tslint:disable-next-line
const wrappedJWT = `{"name":"${NbAuthJWTToken.NAME}","ownerStrategyName":"${jwtToken.getOwnerStrategyName()}","value":"${jwtToken.getValue()}"}`;
// tslint:disable-next-line
const wrappedNonExisting = `{"name":"non-existing","value":"${simpleToken.getValue()}","ownerStrategyName":"${simpleToken.getOwnerStrategyName()}"}`;
const wrappedInvalid = `{"name":"non-existing"`;

describe('default configuration', () => {
Expand Down
7 changes: 5 additions & 2 deletions src/framework/auth/services/token/token-parceler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { NB_AUTH_TOKENS } from '../../auth.options';

export interface NbTokenPack {
name: string,
ownerStrategyName: string,
value: string,
}

Expand All @@ -23,21 +24,23 @@ export class NbAuthTokenParceler {
wrap(token: NbAuthToken): string {
return JSON.stringify({
name: token.getName(),
ownerStrategyName: token.getOwnerStrategyName(),
value: token.toString(),
});
}

unwrap(value: string): NbAuthToken {
let tokenClass: NbAuthTokenClass = this.fallbackClass;
let tokenValue = '';

let tokenOwnerStrategyName = '';
const tokenPack: NbTokenPack = this.parseTokenPack(value);
if (tokenPack) {
tokenClass = this.getClassByName(tokenPack.name) || this.fallbackClass;
tokenValue = tokenPack.value;
tokenOwnerStrategyName = tokenPack.ownerStrategyName;
}

return nbAuthCreateToken(tokenClass, tokenValue);
return nbAuthCreateToken(tokenClass, tokenValue, tokenOwnerStrategyName);
}

// TODO: this could be moved to a separate token registry
Expand Down
5 changes: 3 additions & 2 deletions src/framework/auth/services/token/token-service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,14 @@ import { NB_AUTH_FALLBACK_TOKEN, NbAuthTokenParceler } from './token-parceler';
import { NB_AUTH_TOKENS } from '../../auth.options';

const noop = () => {};
const ownerStrategyName = 'strategy';

describe('token-service', () => {

let tokenService: NbTokenService;
let tokenStorage: NbTokenLocalStorage;
const simpleToken = nbAuthCreateToken(NbAuthSimpleToken, 'test value');
const emptyToken = nbAuthCreateToken(NbAuthSimpleToken, '');
const simpleToken = nbAuthCreateToken(NbAuthSimpleToken, 'test value', ownerStrategyName);
const emptyToken = nbAuthCreateToken(NbAuthSimpleToken, '', ownerStrategyName);
const testTokenKey = 'auth_app_token';

beforeEach(() => {
Expand Down
13 changes: 7 additions & 6 deletions src/framework/auth/services/token/token-storage.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ describe('token-storage', () => {
let tokenParceler: NbAuthTokenParceler;
const testTokenKey = 'auth_app_token';
const testTokenValue = 'test-token';
const ownerStrategyName = 'strategy';

beforeEach(() => {
TestBed.configureTestingModule({
Expand All @@ -44,7 +45,7 @@ describe('token-storage', () => {


it('set test token', () => {
const token = nbAuthCreateToken(NbAuthSimpleToken, testTokenValue);
const token = nbAuthCreateToken(NbAuthSimpleToken, testTokenValue, ownerStrategyName);

tokenStorage.set(token);
expect(localStorage.getItem(testTokenKey)).toEqual(tokenParceler.wrap(token));
Expand All @@ -53,11 +54,11 @@ describe('token-storage', () => {
it('setter set invalid token to localStorage as empty string', () => {
let token;

token = nbAuthCreateToken(NbAuthSimpleToken, null);
token = nbAuthCreateToken(NbAuthSimpleToken, null, ownerStrategyName);
tokenStorage.set(token);
expect(localStorage.getItem(testTokenKey)).toEqual(tokenParceler.wrap(token));

token = nbAuthCreateToken(NbAuthSimpleToken, undefined);
token = nbAuthCreateToken(NbAuthSimpleToken, undefined, ownerStrategyName);
tokenStorage.set(token);
expect(localStorage.getItem(testTokenKey)).toEqual(tokenParceler.wrap(token));
});
Expand All @@ -69,14 +70,14 @@ describe('token-storage', () => {
});

it('should return correct value', () => {
const token = nbAuthCreateToken(NbAuthSimpleToken, 'test');
const token = nbAuthCreateToken(NbAuthSimpleToken, 'test', ownerStrategyName);
localStorage.setItem(testTokenKey, tokenParceler.wrap(token));

expect(tokenStorage.get().getValue()).toEqual(token.getValue());
});

it('clear remove token', () => {
const token = nbAuthCreateToken(NbAuthSimpleToken, 'test');
const token = nbAuthCreateToken(NbAuthSimpleToken, 'test', ownerStrategyName);
localStorage.setItem(testTokenKey, tokenParceler.wrap(token));

tokenStorage.clear();
Expand All @@ -85,7 +86,7 @@ describe('token-storage', () => {
});

it('clear remove token only', () => {
const token = nbAuthCreateToken(NbAuthSimpleToken, 'test');
const token = nbAuthCreateToken(NbAuthSimpleToken, 'test', ownerStrategyName);
localStorage.setItem(testTokenKey, tokenParceler.wrap(token));
localStorage.setItem(testTokenKey + '2', tokenParceler.wrap(token));

Expand Down
22 changes: 11 additions & 11 deletions src/framework/auth/services/token/token.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,16 @@ import { NbAuthOAuth2Token, NbAuthJWTToken, NbAuthSimpleToken } from './token';
describe('auth token', () => {
describe('NbAuthJWTToken', () => {
// tslint:disable
const simpleToken = new NbAuthSimpleToken('token');
const validJWTToken = new NbAuthJWTToken('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzY290Y2guaW8iLCJleHAiOjI1MTczMTQwNjYxNzUsIm5hbWUiOiJDaHJpcyBTZXZpbGxlamEiLCJhZG1pbiI6dHJ1ZX0=.03f329983b86f7d9a9f5fef85305880101d5e302afafa20154d094b229f75773');
const emptyJWTToken = new NbAuthJWTToken('..');
const invalidBase64JWTToken = new NbAuthJWTToken('h%2BHY.h%2BHY.h%2BHY');
const simpleToken = new NbAuthSimpleToken('token','strategy');
const validJWTToken = new NbAuthJWTToken('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzY290Y2guaW8iLCJleHAiOjI1MTczMTQwNjYxNzUsIm5hbWUiOiJDaHJpcyBTZXZpbGxlamEiLCJhZG1pbiI6dHJ1ZX0=.03f329983b86f7d9a9f5fef85305880101d5e302afafa20154d094b229f75773', 'strategy');
const emptyJWTToken = new NbAuthJWTToken('..', 'strategy');
const invalidBase64JWTToken = new NbAuthJWTToken('h%2BHY.h%2BHY.h%2BHY','strategy');

const invalidJWTToken = new NbAuthJWTToken('.');
const invalidJWTToken = new NbAuthJWTToken('.','strategy');

const noExpJWTToken = new NbAuthJWTToken('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzY290Y2guaW8iLCJuYW1lIjoiQ2hyaXMgU2V2aWxsZWphIiwiYWRtaW4iOnRydWV9.03f329983b86f7d9a9f5fef85305880101d5e302afafa20154d094b229f75773');
const noExpJWTToken = new NbAuthJWTToken('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzY290Y2guaW8iLCJuYW1lIjoiQ2hyaXMgU2V2aWxsZWphIiwiYWRtaW4iOnRydWV9.03f329983b86f7d9a9f5fef85305880101d5e302afafa20154d094b229f75773','strategy');

const expiredJWTToken = new NbAuthJWTToken('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzY290Y2guaW8iLCJleHAiOjEzMDA4MTkzODAsIm5hbWUiOiJDaHJpcyBTZXZpbGxlamEiLCJhZG1pbiI6dHJ1ZX0.03f329983b86f7d9a9f5fef85305880101d5e302afafa20154d094b229f75773');
const expiredJWTToken = new NbAuthJWTToken('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzY290Y2guaW8iLCJleHAiOjEzMDA4MTkzODAsIm5hbWUiOiJDaHJpcyBTZXZpbGxlamEiLCJhZG1pbiI6dHJ1ZX0.03f329983b86f7d9a9f5fef85305880101d5e302afafa20154d094b229f75773','strategy');
// tslint:enable

it('getPayload success', () => {
Expand Down Expand Up @@ -71,7 +71,7 @@ describe('auth token', () => {

it('isValid fail', () => {
// without token
expect(new NbAuthJWTToken('').isValid()).toBeFalsy();
expect(new NbAuthJWTToken('', 'strategy').isValid()).toBeFalsy();

// expired date
expect(expiredJWTToken.isValid()).toBeFalsy();
Expand Down Expand Up @@ -123,14 +123,14 @@ describe('auth token', () => {
example_parameter: 'example_value',
};

const validToken = new NbAuthOAuth2Token(token);
const emptyToken = new NbAuthOAuth2Token({});
const validToken = new NbAuthOAuth2Token(token, 'strategy');
const emptyToken = new NbAuthOAuth2Token({}, 'strategy');

const noExpToken = new NbAuthOAuth2Token({
access_token: '2YotnFZFEjr1zCsicMWpAA',
refresh_token: 'tGzv3JOkF0XG5Qx2TlKWIA',
example_parameter: 'example_value',
});
}, 'strategy');

it('getPayload success', () => {
expect(validToken.getPayload()).toEqual(token);
Expand Down
22 changes: 16 additions & 6 deletions src/framework/auth/services/token/token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ export abstract class NbAuthToken {
abstract getValue(): string;
abstract isValid(): boolean;
abstract getPayload(): string;
// the strategy name used to acquire this token (needed for refreshing token)
abstract getOwnerStrategyName(): string;
abstract toString(): string;

getName(): string {
Expand All @@ -17,11 +19,13 @@ export interface NbAuthRefreshableToken {

export interface NbAuthTokenClass {
NAME: string;
new (raw: any): NbAuthToken;
new (raw: any, ownerStrategyName: string): NbAuthToken;
}

export function nbAuthCreateToken(tokenClass: NbAuthTokenClass, token: any) {
return new tokenClass(token);
export function nbAuthCreateToken(tokenClass: NbAuthTokenClass,
token: any,
ownerStrategyName: string) {
return new tokenClass(token, ownerStrategyName);
}

/**
Expand All @@ -31,7 +35,8 @@ export class NbAuthSimpleToken extends NbAuthToken {

static NAME = 'nb:auth:simple:token';

constructor(protected readonly token: any) {
constructor(protected readonly token: any,
protected readonly ownerStrategyName: string) {
super();
}

Expand All @@ -43,6 +48,10 @@ export class NbAuthSimpleToken extends NbAuthToken {
return this.token;
}

getOwnerStrategyName(): string {
return this.ownerStrategyName;
}

getPayload(): string {
return null;
}
Expand Down Expand Up @@ -142,9 +151,10 @@ export class NbAuthOAuth2Token extends NbAuthSimpleToken {

static NAME = 'nb:auth:oauth2:token';

constructor(protected data: { [key: string]: string|number }|string = {}) {
constructor(protected data: { [key: string]: string|number }|string = {},
protected ownerStrategyName: string) {
// we may get it as string when retrieving from a storage
super(prepareOAuth2Token(data));
super(prepareOAuth2Token(data), ownerStrategyName);
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/framework/auth/strategies/auth-strategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export abstract class NbAuthStrategy {
}

createToken(value: any): NbAuthToken {
return nbAuthCreateToken(this.getOption('token.class'), value);
return nbAuthCreateToken(this.getOption('token.class'), value, this.getName());
}

getName(): string {
Expand Down
27 changes: 19 additions & 8 deletions src/framework/auth/strategies/oauth2/oauth2-strategy.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ describe('oauth2-auth-strategy', () => {
error_uri: 'some',
};

const successToken = nbAuthCreateToken(NbAuthOAuth2Token, tokenSuccessResponse) as NbAuthOAuth2Token;
const successToken = nbAuthCreateToken(NbAuthOAuth2Token, tokenSuccessResponse, 'strategy') as NbAuthOAuth2Token;


beforeEach(() => {
Expand Down Expand Up @@ -78,6 +78,7 @@ describe('oauth2-auth-strategy', () => {

beforeEach(() => {
strategy.setOptions({
name: 'strategy',
baseEndpoint: 'http://example.com/',
clientId: 'clientId',
clientSecret: 'clientSecret',
Expand All @@ -104,7 +105,8 @@ describe('oauth2-auth-strategy', () => {
expect(result).toBeTruthy();
expect(result.isSuccess()).toBe(true);
expect(result.isFailure()).toBe(false);
expect(result.getToken()).toEqual(successToken);
expect(result.getToken().getValue()).toEqual(successToken.getValue());
expect(result.getToken().getOwnerStrategyName()).toEqual(successToken.getOwnerStrategyName());
expect(result.getMessages()).toEqual(successMessages);
expect(result.getErrors()).toEqual([]); // no error message, response is success
expect(result.getRedirect()).toEqual('/');
Expand Down Expand Up @@ -166,7 +168,8 @@ describe('oauth2-auth-strategy', () => {
expect(result).toBeTruthy();
expect(result.isSuccess()).toBe(true);
expect(result.isFailure()).toBe(false);
expect(result.getToken()).toEqual(successToken);
expect(result.getToken().getValue()).toEqual(successToken.getValue());
expect(result.getToken().getOwnerStrategyName()).toEqual(successToken.getOwnerStrategyName());
expect(result.getMessages()).toEqual(successMessages);
expect(result.getErrors()).toEqual([]); // no error message, response is success
expect(result.getRedirect()).toEqual('/');
Expand Down Expand Up @@ -205,6 +208,7 @@ describe('oauth2-auth-strategy', () => {

beforeEach(() => {
strategy.setOptions({
name: 'strategy',
baseEndpoint: 'http://example.com/',
clientId: 'clientId',
clientSecret: 'clientSecret',
Expand Down Expand Up @@ -236,7 +240,8 @@ describe('oauth2-auth-strategy', () => {
expect(result).toBeTruthy();
expect(result.isSuccess()).toBe(true);
expect(result.isFailure()).toBe(false);
expect(result.getToken()).toEqual(nbAuthCreateToken(NbAuthOAuth2Token, token));
// tslint:disable-next-line
expect(result.getToken().getValue()).toEqual(nbAuthCreateToken(NbAuthOAuth2Token, token, 'strategy').getValue());
expect(result.getMessages()).toEqual(successMessages);
expect(result.getErrors()).toEqual([]); // no error message, response is success
expect(result.getRedirect()).toEqual('/');
Expand Down Expand Up @@ -266,6 +271,7 @@ describe('oauth2-auth-strategy', () => {

beforeEach(() => {
strategy.setOptions({
name: 'strategy',
baseEndpoint: 'http://example.com/',
clientId: 'clientId',
clientSecret: 'clientSecret',
Expand Down Expand Up @@ -317,7 +323,8 @@ describe('oauth2-auth-strategy', () => {
expect(result).toBeTruthy();
expect(result.isSuccess()).toBe(true);
expect(result.isFailure()).toBe(false);
expect(result.getToken()).toEqual(successToken);
expect(result.getToken().getValue()).toEqual(successToken.getValue());
expect(result.getToken().getOwnerStrategyName()).toEqual(successToken.getOwnerStrategyName());
expect(result.getMessages()).toEqual(successMessages);
expect(result.getErrors()).toEqual([]); // no error message, response is success
expect(result.getRedirect()).toEqual('/success');
Expand All @@ -341,7 +348,8 @@ describe('oauth2-auth-strategy', () => {
expect(result).toBeTruthy();
expect(result.isSuccess()).toBe(true);
expect(result.isFailure()).toBe(false);
expect(result.getToken()).toEqual(successToken);
expect(result.getToken().getValue()).toEqual(successToken.getValue());
expect(result.getToken().getOwnerStrategyName()).toEqual(successToken.getOwnerStrategyName());
expect(result.getMessages()).toEqual(successMessages);
expect(result.getErrors()).toEqual([]); // no error message, response is success
expect(result.getRedirect()).toEqual('/success');
Expand Down Expand Up @@ -376,7 +384,8 @@ describe('oauth2-auth-strategy', () => {
expect(result).toBeTruthy();
expect(result.isSuccess()).toBe(true);
expect(result.isFailure()).toBe(false);
expect(result.getToken()).toEqual(successToken);
expect(result.getToken().getValue()).toEqual(successToken.getValue());
expect(result.getToken().getOwnerStrategyName()).toEqual(successToken.getOwnerStrategyName());
expect(result.getMessages()).toEqual(successMessages);
expect(result.getErrors()).toEqual([]); // no error message, response is success
expect(result.getRedirect()).toEqual('/success');
Expand Down Expand Up @@ -417,6 +426,7 @@ describe('oauth2-auth-strategy', () => {

beforeEach(() => {
strategy.setOptions({
name: 'strategy',
baseEndpoint: 'http://example.com/',
clientId: 'clientId',
clientSecret: 'clientSecret',
Expand All @@ -436,7 +446,8 @@ describe('oauth2-auth-strategy', () => {
expect(result).toBeTruthy();
expect(result.isSuccess()).toBe(true);
expect(result.isFailure()).toBe(false);
expect(result.getToken()).toEqual(successToken);
expect(result.getToken().getValue()).toEqual(successToken.getValue());
expect(result.getToken().getOwnerStrategyName()).toEqual(successToken.getOwnerStrategyName());
expect(result.getMessages()).toEqual(successMessages);
expect(result.getErrors()).toEqual([]); // no error message, response is success
expect(result.getRedirect()).toEqual('/');
Expand Down

0 comments on commit 1c89636

Please sign in to comment.