Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 61 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ On successful sign in the user will be redirect to `restricted`.
- [`.validateToken()`](#validatetoken)
- [`.updatePassword()`](#updatepassword)
- [`.resetPassword()`](#resetpassword)
- [`.signInOAuth()`](#signinoauth)
- [`.processOAuthCallback()`](#processoauthcallback)
- [HTTP Service Wrapper](#http-service-wrapper)
- [Multiple User Types](#multiple-user-types)
- [Route Guards](#route-guards)
Expand Down Expand Up @@ -128,6 +130,12 @@ constructor(private _tokenService: Angular2TokenService) {
resetPasswordPath: 'auth/password',
resetPasswordCallback: window.location.href,

oAuthPaths: {
github: 'auth/github'
},
oAuthCallbackPath: 'oauth_callback',
oAuthWindowType: 'newWindow',

userTypes: null,

globalOptions: {
Expand Down Expand Up @@ -157,7 +165,9 @@ constructor(private _tokenService: Angular2TokenService) {
| `resetPasswordCallback?: string` | Sets the path user are redirected to after email confirmation for password reset |
| `userTypes?: UserTypes[]` | Allows the configuration of multiple user types (see [Multiple User Types](#multiple-user-types)) |
| `globalOptions?: GlobalOptions` | Allows the configuration of global options (see below) |

| `oAuthPaths?: { [key:string]: string }` | Sets paths for sign in with OAuth |
| `oAuthCallbackPath?: string` | Sets path for OAuth sameWindow callback |
| `oAuthWindowType?:`string` | Window type for Oauth authentication |
### Global Options
| Options | Description |
| ------------------------------------- | ----------------------------------------------- |
Expand Down Expand Up @@ -274,6 +284,56 @@ this._tokenService.updatePassword({
);
```

### .signInOAuth()
Initiates OAuth authentication flow. Currently, it supports two window modes:
`newWindow` (default) and `sameWindow` (settable in config as `oAuthWindowType`).
- When `oAuthWindowType` is set to `newWindow`, `.signInOAuth()` opens a new window and returns an observable.

- When `oAuthWindowType` is set to `sameWindow`, `.signInOAuth()` returns nothing and redirects user to auth provider.
After successful authentication, it redirects back to `oAuthCallbackPath`. Application router needs to intercept
this route and call `processOAuthCallback()` to fetch `AuthData` from params.

`signInOAuth(oAuthType: string)`

#### Example:

```javascript
this._tokenService.signInOAuth(
'github'
).subscribe(
res => console.log(res),
error => console.log(error)
);
```

### .processOAuthCallback()
Fetches AuthData from params sent via OAuth redirection in `sameWindow` flow.

`processOAuthCallback()`

#### Example

Callback route:
```javascript
RouterModule.forRoot([
{ path: 'oauth_callback', component: OauthCallbackComponent }
])
```

Callback component:
```javascript
@Component({
template: ''
})
export class OauthCallbackComponent implements OnInit {
constructor(private _tokenService: Angular2TokenService) {}

ngOnInit() {
this._tokenService.processOAuthCallback();
}
}
```

## HTTP Service Wrapper
`Angular2TokenService` wraps all standard Angular2 Http Service calls for authentication and token processing.
If `apiPath` is configured it gets added in front of path.
Expand Down
8 changes: 3 additions & 5 deletions src/angular2-token.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,6 @@ export interface UserType {
path: string;
}

export interface OAuthPaths {
github?: string;
}

export interface GlobalOptions {
headers?: { [key:string]: string; }
}
Expand All @@ -82,7 +78,9 @@ export interface Angular2TokenOptions {

userTypes?: UserType[];

oAuthPaths?: OAuthPaths;
oAuthPaths?: { [key:string]: string; };
oAuthCallbackPath?: string;
oAuthWindowType?: string;

globalOptions?: GlobalOptions;
}
84 changes: 78 additions & 6 deletions src/angular2-token.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ import {
import { ActivatedRoute, Router } from '@angular/router';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/share';
import 'rxjs/add/observable/interval';
import 'rxjs/add/observable/fromEvent';
import 'rxjs/add/operator/pluck';
import 'rxjs/add/operator/filter';

import {
SignInData,
Expand Down Expand Up @@ -106,7 +110,8 @@ export class Angular2TokenService implements CanActivate {
oAuthPaths: {
github: 'auth/github'
},

oAuthCallbackPath: 'oauth_callback',
oAuthWindowType: 'newWindow',
globalOptions: {
headers: {
'Content-Type': 'application/json',
Expand Down Expand Up @@ -165,13 +170,23 @@ export class Angular2TokenService implements CanActivate {

signInOAuth(oAuthType: string) {

let oAuthPath: string;
let oAuthPath: string = this._getOAuthPath(oAuthType);
let callbackUrl: string = `${window.location.origin}/${this._options.oAuthCallbackPath}`;
let oAuthWindowType: string = this._options.oAuthWindowType;
let authUrl: string = this._buildOAuthUrl(oAuthPath, callbackUrl, oAuthWindowType);

if (oAuthType == 'github') {
oAuthPath = this._options.oAuthPaths.github
if (oAuthWindowType == 'newWindow') {
let popup = window.open(authUrl, '_blank', 'closebuttoncaption=Cancel');
return this._requestCredentialsViaPostMessage(popup);
} else if (oAuthWindowType == 'sameWindow') {
window.location.href = authUrl;
} else {
throw `Unsupported oAuthWindowType "${oAuthWindowType}"`;
}
}

window.open(this._constructUserPath() + oAuthPath);
processOAuthCallback() {
this._getAuthDataFromParams();
}

// Sign out request and delete storage
Expand Down Expand Up @@ -374,7 +389,7 @@ export class Angular2TokenService implements CanActivate {
if(this._activatedRoute.queryParams) // Fix for Testing, needs to be removed later
this._activatedRoute.queryParams.subscribe(queryParams => {
let authData: AuthData = {
accessToken: queryParams['token'],
accessToken: queryParams['token'] || queryParams['auth_token'],
client: queryParams['client_id'],
expiry: queryParams['expiry'],
tokenType: 'Bearer',
Expand All @@ -386,6 +401,18 @@ export class Angular2TokenService implements CanActivate {
});
}

private _parseAuthDataFromPostMessage(data: any){
let authData: AuthData = {
accessToken: data['auth_token'],
client: data['client_id'],
expiry: data['expiry'],
tokenType: 'Bearer',
uid: data['uid']
};

this._setAuthData(authData);
}

// Write auth data to storage
private _setAuthData(authData: AuthData) {

Expand Down Expand Up @@ -465,4 +492,49 @@ export class Angular2TokenService implements CanActivate {
else
return this._options.apiPath + '/';
}

private _getOAuthPath(oAuthType: string): string {
let oAuthPath: string;

oAuthPath = this._options.oAuthPaths[oAuthType];
if (oAuthPath == null) {
oAuthPath = `/auth/${oAuthType}`;
}
return oAuthPath;
}

private _buildOAuthUrl(oAuthPath: string, callbackUrl: string, windowType: string): string {
let url: string;

url = `${window.location.origin}/${oAuthPath}`;
url += `?omniauth_window_type=${windowType}`;
url += `&auth_origin_url=${encodeURIComponent(callbackUrl)}`;
if (this._currentUserType != null) {
url += `&resource_class=${this._currentUserType.name}`;
}
return url;
}

private _requestCredentialsViaPostMessage(authWindow: any): Observable<any> {
let poller_observ = Observable.interval(500);
let response_observ = Observable.fromEvent(window, 'message')
.pluck('data')
.filter(this._oauthWindowResponseFilter);

let response_subscription = response_observ.subscribe(this._parseAuthDataFromPostMessage.bind(this));
let poller_subscription = poller_observ.subscribe(() => {
if (authWindow.closed) {
poller_subscription.unsubscribe();
} else {
authWindow.postMessage('requestCredentials', '*');
}
});
return response_observ;
}

private _oauthWindowResponseFilter(data: any) {
if(data.message == 'deliverCredentials' || data.message == 'authFailure') {
return data;
}
}
}