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

Initial commit: WIP

parents
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"project": {
"name": "angular-aap-auth"
},
"apps": [
{
"root": "src",
"outDir": "dist",
"assets": [
"assets",
"favicon.ico"
],
"index": "index.html",
"main": "main.ts",
"polyfills": "polyfills.ts",
"test": "test.ts",
"tsconfig": "tsconfig.app.json",
"testTsconfig": "tsconfig.spec.json",
"prefix": "app",
"styles": [
"styles.css"
],
"scripts": [],
"environmentSource": "environments/environment.ts",
"environments": {
"dev": "environments/environment.ts",
"prod": "environments/environment.prod.ts"
}
}
],
"e2e": {
"protractor": {
"config": "./protractor.conf.js"
}
},
"lint": [
{
"project": "src/tsconfig.app.json",
"exclude": "**/node_modules/**"
},
{
"project": "src/tsconfig.spec.json",
"exclude": "**/node_modules/**"
},
{
"project": "e2e/tsconfig.e2e.json",
"exclude": "**/node_modules/**"
}
],
"test": {
"karma": {
"config": "./karma.conf.js"
}
},
"defaults": {
"styleExt": "css",
"component": {}
}
}
# Editor configuration, see http://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
max_line_length = off
trim_trailing_whitespace = false
# See http://help.github.com/ignore-files/ for more about ignoring files.
# compiled output
/dist
/dist-server
/tmp
/out-tsc
# dependencies
/node_modules
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
# misc
/.sass-cache
/connect.lock
/coverage
/libpeerconnection.log
npm-debug.log
testem.log
/typings
# e2e
/e2e/*.js
/e2e/*.map
# System Files
.DS_Store
Thumbs.db
# NgAapAuth
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 1.6.6.
## Development server
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
## Code scaffolding
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
## Build
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `-prod` flag for a production build.
## Running unit tests
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
## Running end-to-end tests
Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
## Further help
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
import { AppPage } from './app.po';
describe('ng-aap-auth App', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
});
it('should display welcome message', () => {
page.navigateTo();
expect(page.getParagraphText()).toEqual('Welcome to app!');
});
});
import { browser, by, element } from 'protractor';
export class AppPage {
navigateTo() {
return browser.get('/');
}
getParagraphText() {
return element(by.css('app-root h1')).getText();
}
}
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/e2e",
"baseUrl": "./",
"module": "commonjs",
"target": "es5",
"types": [
"jasmine",
"jasminewd2",
"node"
]
}
}
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular/cli'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular/cli/plugins/karma')
],
client:{
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
reports: [ 'html', 'lcovonly' ],
fixWebpackSourcePaths: true
},
angularCli: {
environment: 'dev'
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false
});
};
{
"$schema": "./node_modules/ng-packagr/ng-package.schema.json",
"lib": {
"entryFile": "public_api.ts"
}
}
{
"name": "ng-aap-auth",
"version": "0.1.0",
"license": "MIT",
"scripts": {
"ng": "ng",
"start": "ng serve --aot",
"build": "ng build --prod --aot",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e",
"packagr": "ng-packagr -p ng-package.json"
},
"private": true,
"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/cli": "1.6.6",
"@angular/compiler-cli": "^5.2.2",
"@angular/language-service": "^5.2.2",
"@types/jasmine": "~2.8.6",
"@types/jasminewd2": "~2.0.3",
"@types/node": "~9.4.0",
"codelyzer": "^4.1.0",
"jasmine-core": "~2.9.1",
"jasmine-spec-reporter": "~4.2.1",
"karma": "~2.0.0",
"karma-chrome-launcher": "~2.2.0",
"karma-coverage-istanbul-reporter": "^1.4.1",
"karma-jasmine": "~1.1.1",
"karma-jasmine-html-reporter": "^0.2.2",
"ncu": "^0.2.1",
"ng-packagr": "^1.6.0",
"protractor": "~5.3.0",
"ts-node": "~4.1.0",
"tslint": "~5.9.1",
"typescript": "~2.6.2"
}
}
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
const { SpecReporter } = require('jasmine-spec-reporter');
exports.config = {
allScriptsTimeout: 11000,
specs: [
'./e2e/**/*.e2e-spec.ts'
],
capabilities: {
'browserName': 'chrome'
},
directConnect: true,
baseUrl: 'http://localhost:4200/',
framework: 'jasmine',
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 30000,
print: function() {}
},
onPrepare() {
require('ts-node').register({
project: 'e2e/tsconfig.e2e.json'
});
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
}
};
export * from './src/app/modules/auth/auth.module'
.spread {
display: flex;
justify-content: space-between;
}
<!--The content below is only a placeholder and can be replaced.-->
<div style="text-align:center">
<h1>
Auth testing app
</h1>
</div>
<div class="spread">
<button (click)="auth.windowOpen({ttl: '1'})">Login small window</button>
<button (click)="auth.tabOpen({ttl: '1'})" target="_blank">Login new tab</button>
<button (click)="auth.logOut()" target="_blank">Logout</button>
</div>
<div>
<p>Authenticated: {{ isAuthenticated|async }}</p>
<p>Real name: {{ (credentials|async).realname }}</p>
<!-- Trying alternative, more direct access -->
<p>Username: {{ username|async }}</p>
<p>Expiration: {{ (credentials|async).expiration }}</p>
<p>Token: {{ (credentials|async).token }}</p>
</div>
import { TestBed, async } from '@angular/core/testing';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
AppComponent
],
}).compileComponents();
}));
it('should create the app', async(() => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app).toBeTruthy();
}));
it(`should have as title 'app'`, async(() => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app.title).toEqual('app');
}));
it('should render title in a h1 tag', async(() => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.debugElement.nativeElement;
expect(compiled.querySelector('h1').textContent).toContain('Welcome to app!');
}));
});
import {
Component,
OnInit
} from '@angular/core';
import {
Observable,
} from 'rxjs/observable';
import {
map
} from 'rxjs/operators';
import {
AuthService,
Credentials
} from 'app/modules/auth/auth.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
authURL = 'https://api.aai.ebi.ac.uk';
credentials: Observable < Credentials > ;
username: Observable < string | null > ;
isAuthenticated: Observable < string > ;
constructor(
public auth: AuthService,
) {
this.credentials = auth.credentials$;
this.username = auth.username$;
this.isAuthenticated = (auth.isAuthenticated$).pipe(
map(value => value && 'true' || 'false')
);
}
ngOnInit() {
// Register and unregister login events
this.auth.addLogInEventListener(() => console.log('Welcome'));
const firstEventID = this.auth.addLogInEventListener(() => console.log('This should not be visible'));
this.auth.removeLogInEventListener(firstEventID);
this.auth.addLogInEventListener(() => alert('Welcome'));
const secondEventID = this.auth.addLogInEventListener(() => alert('This should never be displayed'));
this.auth.removeLogInEventListener(secondEventID);
// Register and unregister logout events
this.auth.addLogOutEventListener(() => console.log('Bye'));
const thirdEventID = this.auth.addLogOutEventListener(() => console.log('This should not be visible'));
this.auth.removeLogOutEventListener(thirdEventID);
this.auth.addLogOutEventListener(() => alert('Bye'));
const fourthEventID = this.auth.addLogOutEventListener(() => alert('This should never be displayed'));
this.auth.removeLogOutEventListener(fourthEventID);
}
}
import {
BrowserModule
} from '@angular/platform-browser';
import {
NgModule
} from '@angular/core';
import {
AppComponent
} from './app.component';
import {
AuthModule
} from './modules/auth/auth.module';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AuthModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule {}
import {
NgModule
} from '@angular/core';
import {
CommonModule
} from '@angular/common';
import {
AuthService
} from './auth.service';
import {
TokenService
} from './token.service';
import {
// JwtModule,
JwtHelperService
} from '@auth0/angular-jwt';
export let tokenName = 'id_token';
// export function getToken(): string | null {
// return localStorage.getItem(tokenName) || null;
// }
@NgModule({
imports: [
CommonModule,
// JwtModule.forRoot({
// config: {
// tokenGetter: getToken,
// whitelistedDomains: []
// }
// })
],
providers: [
AuthService,
TokenService,
JwtHelperService
]
})
export class AuthModule {}
import { TestBed, inject } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { spyOnClass } from 'jasmine-es6-spies';
import { AuthConfig } from './index';
import { AuthService } from './auth.service';
import { TokenService } from './token.service';
class APP_CONFIG implements AuthConfig{
authURL: "something";
}
class DummyComponent {}
describe('Service: Auth', () => {
// secret sauce which we inject into the tests
const tokenerSpy = spyOnClass(TokenService);
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
RouterTestingModule.withRoutes([
{ path: '', component: DummyComponent }
])
],
providers: [
APP_CONFIG,
{ provide: TokenService, useValue: tokenerSpy },
AuthService
]
});
});
it('should ...', inject([AuthService], (service: AuthService) => {
expect(service).toBeTruthy();
}));
it('should check token to see if user is logged in',
inject([AuthService], (service: AuthService) => {
tokenerSpy.isTokenExpired.and.returnValues(true, false);
expect(service.loggedIn()).toBeFalsy();
expect(service.loggedIn()).toBeTruthy();
expect(tokenerSpy.isTokenExpired).toHaveBeenCalledTimes(2);
}));
});
import {
Injectable,
RendererFactory2
} from '@angular/core';
import {
Observable
} from 'rxjs/observable';
import {
BehaviorSubject,
} from 'rxjs/behaviorsubject';
import {
filter,
map
} from 'rxjs/operators';
import {
TokenService
} from './token.service';
// TODO: remove dependency
export let authURL = 'https://api.aai.ebi.ac.uk';
export interface Credentials {
realname: string | null;
username: string | null;
token: string | null;
expiration: Date | undefined;
}
interface LoginOptions {
[key: string]: string
}
@Injectable()
export class AuthService {
static emptyCredentials: Credentials = {
realname: null,
username: null,
token: null,
expiration: undefined
}
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));
private _isAuthenticated = new BehaviorSubject < boolean > (false);
public isAuthenticated$ = this._isAuthenticated.asObservable();
private _loginCallbacks: Function[] = [];
private _logoutCallbacks: Function[] = [];
private _timeoutID: number;
readonly domain: string;
constructor(
private rendererFactory: RendererFactory2,
private tokener: TokenService,
// @Inject('AAP_CONFIG') private environment: AuthConfig,