Commit 9d17a291 authored by Eduardo Sanz García's avatar Eduardo Sanz García
Browse files

refactor: removed tokenservice

chore: added list of authors.
parent a4e78c21
# Original author
Pau Ruiz i Safont <psafont@ebi.ac.uk>
# Maintainer and contributor
Eduardo Sanz <eduardo@ebi.ac.uk>
{
"name": "ng-aap-auth",
"version": "0.1.0",
"license": "MIT",
"name": "angular-aap-auth",
"version": "0.2.0",
"license": "Apache-2.0",
"scripts": {
"ng": "ng",
"start": "ng serve --aot",
......@@ -11,25 +11,34 @@
"e2e": "ng e2e",
"packagr": "ng-packagr -p ng-package.json"
},
"private": true,
"private": false,
"keywords": [
"angular",
"jwt",
"auth"
],
"repository": {
"type": "git",
"url": "https://gitlab.ebi.ac.uk/tools-glue/angular-aap-auth.git"
},
"bugs": {
"url": "https://gitlab.ebi.ac.uk/tools-glue/angular-aap-auth/issues"
},
"dependencies": {
"@angular/animations": "^5.2.2",
"@angular/common": "^5.2.2",
"@angular/compiler": "^5.2.2",
"@angular/core": "^5.2.2",
"@angular/forms": "^5.2.2",
"@angular/platform-browser": "^5.2.2",
"@angular/platform-browser-dynamic": "^5.2.2",
"@angular/router": "^5.2.2",
"@auth0/angular-jwt": "^1.0.0-beta.9",
"core-js": "^2.5.3",
"rxjs": "^5.5.6",
"zone.js": "^0.8.20"
},
"devDependencies": {
"@angular/common": "^5.2.2",
"@angular/compiler": "^5.2.2",
"@angular/cli": "1.6.6",
"@angular/compiler-cli": "^5.2.2",
"@angular/language-service": "^5.2.2",
"@angular/platform-browser": "^5.2.2",
"@angular/platform-browser-dynamic": "^5.2.2",
"@types/jasmine": "~2.8.6",
"@types/jasminewd2": "~2.0.3",
"@types/node": "~9.4.0",
......
......@@ -9,33 +9,35 @@ import {
AuthService
} from './auth.service';
import {
TokenService
} from './token.service';
import {
// JwtModule,
// JwtModule,
JWT_OPTIONS,
JwtHelperService
} from '@auth0/angular-jwt';
export let tokenName = 'id_token';
// export function getToken(): string | null {
// return localStorage.getItem(tokenName) || null;
// }
export function getToken(): string | null {
return localStorage.getItem(tokenName);
}
@NgModule({
imports: [
CommonModule,
// JwtModule.forRoot({
// config: {
// tokenGetter: getToken,
// whitelistedDomains: []
// }
// })
// JwtModule.forRoot({
// config: {
// tokenGetter: getToken,
// whitelistedDomains: []
// }
// })
],
providers: [
AuthService,
TokenService,
{
provide: JWT_OPTIONS,
useValue: {
tokenGetter: getToken,
}
},
JwtHelperService
]
})
export class AuthModule {}
}) export class AuthModule {}
......@@ -14,17 +14,18 @@ import {
} from 'rxjs/operators';
import {
TokenService
} from './token.service';
JwtHelperService
} from '@auth0/angular-jwt';
// TODO: remove dependency
export let authURL = 'https://api.aai.ebi.ac.uk';
let tokenName = 'id_token';
let authURL = 'https://api.aai.ebi.ac.uk';
export interface Credentials {
realname: string | null;
username: string | null;
token: string | null;
expiration: Date | undefined;
expiration: Date | null;
}
interface LoginOptions {
......@@ -38,16 +39,16 @@ export class AuthService {
realname: null,
username: null,
token: null,
expiration: undefined
expiration: null
}
private _credentials = new BehaviorSubject < Credentials > (AuthService.emptyCredentials);
public credentials$ = this._credentials.asObservable();
public realname$ = this.credentials$.pipe(map(credentials => credentials.realname));
public username$ = this.credentials$.pipe(map(credentials => credentials.username));
public token$ = this.credentials$.pipe(map(credentials => credentials.token));
public expiration$ = this.credentials$.pipe(map(credentials => credentials.expiration));
public realname$ = this.credentials$.pipe(map(credentials => credentials.realname));
public username$ = this.credentials$.pipe(map(credentials => credentials.username));
public token$ = this.credentials$.pipe(map(credentials => credentials.token));
public expiration$ = this.credentials$.pipe(map(credentials => credentials.expiration));
private _isAuthenticated = new BehaviorSubject < boolean > (false);
public isAuthenticated$ = this._isAuthenticated.asObservable();
......@@ -59,7 +60,7 @@ export class AuthService {
constructor(
private rendererFactory: RendererFactory2,
private tokener: TokenService,
private jwt: JwtHelperService
// @Inject('AAP_CONFIG') private environment: AuthConfig,
) {
this.domain = encodeURIComponent(window.location.origin);
......@@ -76,7 +77,7 @@ export class AuthService {
if (!this.messageIsAcceptable(event)) {
return;
}
this.tokener.saveToken(event.data);
localStorage.setItem(tokenName, event.data);
event.source.close();
this._updateCredentials();
......@@ -98,10 +99,9 @@ export class AuthService {
window.clearTimeout(this._timeoutID);
}
// coercing dates to numbers with the unary operator '+'
const delay = +<Date>this.tokener.getExpiration() - +new Date();
const delay = + < Date > this.jwt.getTokenExpirationDate() - +new Date();
this._timeoutID = window.setTimeout(this.logOut.bind(this), delay);
}
else {
} else {
this._isAuthenticated.next(isAuthenticated);
this._credentials.next(AuthService.emptyCredentials);
}
......@@ -113,7 +113,11 @@ export class AuthService {
* authenticated requests or not.
*/
public loggedIn(): boolean {
return !this.tokener.isTokenExpired();
try {
return !this.jwt.isTokenExpired();
} catch (error) {
return false
}
}
private _getCredentials(): Credentials {
......@@ -125,24 +129,24 @@ export class AuthService {
};
}
public getUserName(): string | null{
return this.tokener.getClaim<string, null>('email', null);
public getUserName(): string | null {
return this.getClaim < string, null > ('email', null);
}
public getName(): string | null{
return this.tokener.getClaim<string, null>('name', null);
public getName(): string | null {
return this.getClaim < string, null > ('name', null);
}
public getToken(): string {
return this.tokener.getToken();
public getToken(): string | null {
return this.jwt.tokenGetter();
}
public getExpiration(): Date | undefined{
return this.tokener.getExpiration();
public getExpiration(): Date | null {
return this.jwt.getTokenExpirationDate();
}
public logOut(): void {
this.tokener.removeToken();
localStorage.removeItem(tokenName);
this._updateCredentials();
this._logoutCallbacks.map(callback => callback && callback());
if (this._timeoutID) {
......@@ -281,4 +285,20 @@ export class AuthService {
const expectedURL: string = authURL.replace(/\/$/, '');
return event.origin === expectedURL;
}
/**
* Get claims from the token.
*
* @param {string} The name of the claim
* @param {any} The default value in case of error
*
* @returnType { any } Claim
*/
public getClaim<T, C>(claim:string, defaultValue: C): T|C{
try {
return <T>this.jwt.decodeToken()[claim];
} catch (e) {
return defaultValue;
}
}
}
import {
TestBed,
inject
} from '@angular/core/testing';
import {
JwtHelperService
} from '@auth0/angular-jwt';
import {
TokenService
} from './token.service';
import {
spyOnClass
} from 'jasmine-es6-spies';
describe('Service: Token', () => {
const jwterSpy = spyOnClass(JwtHelper);
const fakeToken = {
'email': 'test@ebi.ac.uk',
'name': 'Jeff'
};
beforeEach(() => {
TestBed.configureTestingModule({
providers: [{
provide: JwtHelperService,
useValue: jwterSpy
},
TokenService
]
});
});
it('should instantiate', inject(
[TokenService], (service: TokenService) => {
expect(service).toBeTruthy();
}));
it('should save tokens', inject(
[TokenService], (service: TokenService) => {
localStorage.clear();
service.saveToken('one');
expect(service.getToken()).toBe('one');
service.saveToken('another');
expect(service.getToken()).toBe('another');
localStorage.clear();
}));
it('should use the jwt helper to check if a token is expired', inject(
[TokenService], (service: TokenService) => {
jwterSpy.isTokenExpired.and.returnValues(true, false);
expect(service.isTokenExpired()).toBeTruthy();
expect(service.isTokenExpired()).toBeFalsy();
expect(jwterSpy.isTokenExpired).toHaveBeenCalledTimes(2);
}));
it('should use the jwt helper to check retrieve the claims', inject(
[TokenService], (service: TokenService) => {
jwterSpy.decodeToken.and.returnValue(fakeToken);
expect(service.getEmail()).toBe(fakeToken['email']);
expect(service.getName()).toBe(fakeToken['name']);
expect(jwterSpy.decodeToken).toHaveBeenCalledTimes(2);
}));
it('should not throw exceptions when getting claims from invalid tokens', inject(
[TokenService], (service: TokenService) => {
jwterSpy.decodeToken.and.throwError('dodgyToken');
expect(service.getName()).toBe('');
}));
});
import {
Injectable
} from '@angular/core';
import {
JwtHelperService
} from '@auth0/angular-jwt';
let tokenName = 'id_token';
@Injectable()
export class TokenService {
constructor(
private jwter: JwtHelperService
) {}
public getToken(): string{
return localStorage.getItem(tokenName) || '';
}
public saveToken(jwt: string) {
localStorage.setItem(tokenName, jwt);
}
public removeToken() {
localStorage.removeItem(tokenName);
}
// public getEmail(): string {
// return this.getClaim('email');
// }
// public getName(): string {
// return this.getClaim('name');
// }
public getExpiration(): Date | undefined{
return this.jwter.getTokenExpirationDate(this.getToken());
}
public isTokenExpired(): boolean {
return this.jwter.isTokenExpired(this.getToken());
}
// private getClaim(claim: string): string {
// return this.processToken(
// (token: string) => this.jwter.decodeToken(token)[claim],
// '');
// }
public getClaim<T, C>(claim:string, defaultValue: C): T|C{
const token = this.getToken();
try {
return <T>this.jwter.decodeToken(token)[claim];
} catch (e) {
return defaultValue;
}
}
// /**
// * Try to get some information from the token, if it fails return a default value.
// *
// * @param {process} function to apply to the token
// * @param {defaultValue} default value to return if something goes wrong while applying the function.
// * @returnType { T } the type that the function returns and the default value.
// */
// private processToken < T > (process: (token: string) => T, defaultValue: T): T {
// const token = this.getToken();
// try {
// return process(token);
// } catch (e) {
// return defaultValue;
// }
// }
}
......@@ -27,12 +27,6 @@
"@ngtools/json-schema" "^1.1.0"
rxjs "^5.5.6"
"@angular/animations@^5.2.2":
version "5.2.2"
resolved "https://registry.yarnpkg.com/@angular/animations/-/animations-5.2.2.tgz#3364a0c4f355d3313dda9bde526e376c137fa169"
dependencies:
tslib "^1.7.1"
"@angular/cli@1.6.6":
version "1.6.6"
resolved "https://registry.yarnpkg.com/@angular/cli/-/cli-1.6.6.tgz#002119ab2ed804bbdc86075e0095eadda2a0baa0"
......@@ -124,12 +118,6 @@
dependencies:
tslib "^1.7.1"
"@angular/forms@^5.2.2":
version "5.2.2"
resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-5.2.2.tgz#eca24f15d96de285cd0726601db4bffec39c01f3"
dependencies:
tslib "^1.7.1"
"@angular/language-service@^5.2.2":
version "5.2.2"
resolved "https://registry.yarnpkg.com/@angular/language-service/-/language-service-5.2.2.tgz#2829214885096c4168566a9f74364a8fed641a49"
......@@ -146,12 +134,6 @@
dependencies:
tslib "^1.7.1"
"@angular/router@^5.2.2":
version "5.2.2"
resolved "https://registry.yarnpkg.com/@angular/router/-/router-5.2.2.tgz#b0ffd7121290e8c01f20862b4a2638ebcebc61cf"
dependencies:
tslib "^1.7.1"
"@angular/tsc-wrapped@^4.4.5":
version "4.4.6"
resolved "https://registry.yarnpkg.com/@angular/tsc-wrapped/-/tsc-wrapped-4.4.6.tgz#16787cbbf50bdc7e738123b19c32527f244e178d"
......
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