README.md 7.98 KB
Newer Older
1
# ng-ebi-authorization
Eduardo Sanz García's avatar
Eduardo Sanz García committed
2

3 4
The ng-ebi-authorization is a simple authentication Angular library that relies
on EBI's Authentication and Authorization Profile (AAP) infrastructure. After
5 6
successful login, a JWT token is stored on the browser (via cookie, local or
session storage).
Eduardo Sanz García's avatar
Eduardo Sanz García committed
7

8
## Installation
Eduardo Sanz García's avatar
Eduardo Sanz García committed
9

10
To install this library, run:
Eduardo Sanz García's avatar
Eduardo Sanz García committed
11

12
```
13
npm install --save ng-ebi-authorization @auth0/angular-jwt
Eduardo Sanz García's avatar
Eduardo Sanz García committed
14

15
or
Eduardo Sanz García's avatar
Eduardo Sanz García committed
16

17
yarn add ng-ebi-authorization @auth0/angular-jwt
18
```
Eduardo Sanz García's avatar
Eduardo Sanz García committed
19

20 21
Compatibility table

22
Angular version | ng-ebi-authorization version
23
--- | ---
24 25 26 27
>=5 <6 | <= angular-aap-auth@1.0.0-alpha.7 (deprecated)
>=6 <7 | >= angular-aap-auth@1.0.0-alpha.8 (deprecated) or ng-ebi-authorization@1.0.0-beta.1

ng-ebi-authorization is an updated version of angular-aap-auth.
28

29
## Consuming the library
Eduardo Sanz García's avatar
Eduardo Sanz García committed
30

31 32 33 34 35 36 37 38 39
The library exposes user information through `User` objects, which have information that's usually required for web application to work:

- The unique identifier (`uid`): if a unique identifier has to be used, use this field.
- Name (`name`): the full name of the user, for display purposes
- Nickname (`nickname`): if the user is an local aap account, it will contain the username, otherwise will have a weird string.
- Email (`email`): the account's email, this is for information only and several accounts may have the same username.
- Domains (`domains`): not directly provided. They may be misused into dong checking if the user has authorization to do some actions.
  This should be done always server-side, if the domains information wants to be shown to the user as information it can still be done,
  check the [Advanced usage](#advanced-usage) to see how to expose arbitrary token claims, or the embedded app.
40
In your Angular `AppModule` (app.module.ts):
Eduardo Sanz García's avatar
Eduardo Sanz García committed
41

42 43 44 45 46 47 48
```typescript
import {
    BrowserModule
} from '@angular/platform-browser';
import {
    NgModule
} from '@angular/core';
Eduardo Sanz García's avatar
Eduardo Sanz García committed
49

50 51
import {
    AuthModule
52
} from 'ng-ebi-authorization';
53 54 55
import {
    JwtModule
} from '@auth0/angular-jwt';
56

57 58 59 60
import {
    AppComponent
} from './app.component';

61 62 63 64 65 66
@NgModule({
    declarations: [
        AppComponent
    ],
    imports: [
        BrowserModule,
67
        AuthModule.forRoot(), // Defaults to localStorage `id_token` key.
68
        JwtModule.forRoot({
69
            config: {
70
                tokenGetter: () => localStorage.getItem('id_token')
71 72
            }
        })
73
    ],
74
    providers: [],
75 76 77 78 79 80
    bootstrap: [AppComponent]
})
export class AppModule {}
```

The default configuration uses localStorage to save the JWT token under the key
81
'id_token'. See [Advanced usage](#advanced-usage) for a more fine grained configuration.
82 83 84

Example use on a component:

85 86 87 88 89 90 91
```typescript
import {
    Component,
    OnInit
} from '@angular/core';
import {
    Observable,
92
} from 'rxjs';
93 94 95

import {
    AuthService,
96
    User
97
} from 'ng-ebi-authorization';
98 99 100 101 102

@Component({
    selector: 'app-root',
    template: `
    <button (click)="auth.windowOpen()">Login small window</button>
103 104
    <button (click)="auth.tabOpen()">Login new tab</button>
    <button (click)="auth.logOut()">Logout</button>
105

106
    <div *ngIf="user | async; else loggedOut">
107 108
        <p>Name: {{ user.name }}</p>
        <p>Unique Identifier: {{ user.uid }}</p>
109
        <p>Email: {{ user.email }}</p>
110 111 112 113 114 115 116 117
        <p>Token: {{ user.token }}</p>
    </div>
    <ng-template #loggedOut>
        <p>Please, log in.</p>
    </ng-template>
    `
})
export class AppComponent implements OnInit {
118
    user: Observable < User | null > ;
119 120 121 122 123

    constructor(
        // Public for demonstration purposes
        public auth: AuthService,
    ) {
124
        this.user = auth.user();
125 126 127 128 129 130 131 132 133
    }

    ngOnInit() {
        this.auth.addLogInEventListener(() => console.log('Welcome'));
        this.auth.addLogOutEventListener(() => console.log('Bye'));
    }
}
```

134
## Advanced usage
135

136
Advanced module configuration:
137 138 139 140 141 142 143 144 145 146 147 148 149 150

```typescript
import {
    BrowserModule
} from '@angular/platform-browser';
import {
    NgModule
} from '@angular/core';

import {
    AppComponent
} from './app.component';
import {
    AuthModule
151
} from 'ng-ebi-authorization';
152 153 154
import {
    JwtModule
} from '@auth0/angular-jwt';
155 156 157 158 159 160 161

export function getToken(): string {
    return localStorage.getItem('jwt_token') || '';
}
export function updateToken(newToken: string): void {
    return localStorage.setItem('jwt_token', newToken);
}
162 163 164 165
// Optional
export function removeToken(): void {
    return localStorage.removeItem('jwt_token');
}
166 167 168 169 170 171 172 173 174 175 176

@NgModule({
    declarations: [
        AppComponent
    ],
    imports: [
        BrowserModule,
        AuthModule.forRoot({
            aapURL: 'https://api.aai.ebi.ac.uk',
            tokenGetter: getToken,
            tokenUpdater: updateToken,
177
            tokenRemover: removeToken // Optional
178
        }),
179
        JwtModule.forRoot({
180 181 182 183
            config: {
                tokenGetter: getToken,
            }
        })
184
    ],
185
    providers: [],
186 187 188 189 190 191 192 193 194 195 196 197 198
    bootstrap: [AppComponent]
})
export class AppModule {}
```

Example on how to get specific claims:

```typescript
import {
    Component,
    OnInit
} from '@angular/core';
import {
199
    Observable
200
} from 'Observable';
201 202 203 204 205 206
import {
    map
} from 'rxjs/operators';

import {
    AuthService,
207
    TokenService // Needed for JWT claim introspection
208
} from 'ng-ebi-authorization';
209

210 211 212 213
import {
    JwtHelperService,
} from '@auth0/angular-jwt';

214 215
@Component({
    selector: 'app-root',
216 217
    template: `
    <button (click)="openLoginWindow()">Login small window</button>
218
    <button (click)="logOut()">Logout</button>
219

220 221 222 223 224 225 226 227
    <div *ngIf="(user | async) as user; else loggedOut">
        <p>Expiration Date: {{ expiration | async }}</p>
        <p>Issuer: {{ iss | async }}</p>

    </div>
    <ng-template #loggedOut>
        <p>Please, log in.</p>
    </ng-template>
228
    `
229 230
})
export class AppComponent implements OnInit {
231
    user: Observable < User | null > ;
232 233 234

    // How to obtain other claims
    expiration: Observable < Date | null > ;
235
    iss: Observable < string | null > ;
236 237 238

    constructor(
        // Public for demonstration purposes
239
        private auth: AuthService,
240
        private jwt: JwtHelperService
241
    ) {
242 243 244
        this.user = auth.user();

        this.expiration = this.user.pipe(
245
            map(user => {
246
                try {
247
                    return jwt.getTokenExpirationDate(<string>user.token);
248 249 250 251
                } catch (e) {
                    return null;
                }
            })
252 253
        );

254 255
        this.iss = this.user.pipe(
            map(_ => jwt.getClaim < string, null > ('iss', null))
256 257 258
        );
    }

259 260 261 262 263 264 265 266 267 268 269
    openLoginWindow() {
        // ttl: time of live, and location
        this.auth.windowOpen({
            'ttl': '1'
        }, 500, 500, 100, 100);
    }

    logOut() {
        this.auth.logOut();
    }

270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289
    ngOnInit() {
        // Demonstration of 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);

        // Demonstration of 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);
    }
}
```

290 291 292 293 294
## Want to help?

Want to file a bug, contribute some code, or improve documentation? Excellent!
Read up on our guidelines for [contributing][contributing].

295 296 297
## License

Apache 2.0 © [EMBL - European Bioinformatics Institute](https://www.ebi.ac.uk/about/terms-of-use)
298
[contributing]: https://gitlab.ebi.ac.uk/tools-glue/ng-ebi-authorization/blob/master/CONTRIBUTING.md