Last active
July 9, 2018 09:26
-
-
Save JBreit/4884d04e2d110dc1e9be976ccfcd2a40 to your computer and use it in GitHub Desktop.
@ngrx/store authentication example code
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { createFeatureSelector } from '@ngrx/store'; | |
import * as auth from './reducers/auth.reducers'; | |
export interface AppState { | |
authState: auth.State; | |
} | |
export const reducers = { | |
auth: auth.reducer | |
}; | |
export const selectAuthState = createFeatureSelector<AppState>('auth'); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Action } from '@ngrx/store'; | |
import { User } from '../../models/user'; | |
export const LOGIN = '[Auth] Login'; | |
export const LOGIN_SUCCESS = '[Auth] Login Success'; | |
export const LOGIN_FAILURE = '[Auth] Login Failure'; | |
export const LOGIN_REDIRECT = '[Auth] Login Redirect'; | |
export const REGISTER = '[Auth] Register'; | |
export const REGISTER_SUCCESS = '[Auth] Register Success'; | |
export const REGISTER_FAILURE = '[Auth] Register Failure'; | |
export const LOGOUT = '[Auth] Logout'; | |
export const GET_STATUS = '[Auth] GetStatus'; | |
/** | |
* Authenticate | |
* | |
* @class Login | |
* @implements { Action } | |
*/ | |
export class Login implements Action { | |
readonly type = LOGIN; | |
constructor(public payload: User) {} | |
} | |
/** | |
* Authenticate | |
* | |
* @class LoginSuccess | |
* @implements { Action } | |
*/ | |
export class LoginSuccess implements Action { | |
readonly type = LOGIN_SUCCESS; | |
constructor(public payload: User) {} | |
} | |
/** | |
* Authenticate | |
* | |
* @class LoginFailure | |
* @implements { Action } | |
*/ | |
export class LoginFailure implements Action { | |
readonly type = LOGIN_FAILURE; | |
constructor(public payload: { error: string }) {} | |
} | |
/** | |
* Authenticate | |
* | |
* @class LoginRedirect | |
* @implements { Action } | |
*/ | |
export class LoginRedirect implements Action { | |
readonly type = LOGIN_REDIRECT; | |
} | |
/** | |
* Authenticate | |
* | |
* @class Register | |
* @implements { Action } | |
*/ | |
export class Register implements Action { | |
readonly type = REGISTER; | |
constructor(public payload: User) {} | |
} | |
/** | |
* Authenticate | |
* | |
* @class RegisterSuccess | |
* @implements { Action } | |
*/ | |
export class RegisterSuccess implements Action { | |
readonly type = REGISTER_SUCCESS; | |
constructor(public payload: User) {} | |
} | |
/** | |
* Authenticate | |
* | |
* @class RegisterFailure | |
* @implements { Action } | |
*/ | |
export class RegisterFailure implements Action { | |
readonly type = REGISTER_FAILURE; | |
constructor(public payload: { error: string }) {} | |
} | |
/** | |
* Authenticate | |
* | |
* @class Logout | |
* @implements { Action } | |
*/ | |
export class Logout implements Action { | |
readonly type = LOGOUT; | |
} | |
/** | |
* Authenticate | |
* | |
* @class GetStatus | |
* @implements { Action } | |
*/ | |
export class GetStatus implements Action { | |
readonly type = GET_STATUS; | |
} | |
/** | |
* Actions type | |
* | |
* @type { Actions } | |
*/ | |
export type All = | |
| Login | |
| LoginSuccess | |
| LoginFailure | |
| LoginRedirect | |
| Register | |
| RegisterSuccess | |
| RegisterFailure | |
| Logout | |
| GetStatus; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Injectable } from '@angular/core'; | |
import { Action } from '@ngrx/store'; | |
import { Router } from '@angular/router'; | |
import { HttpClient } from '@angular/common/http'; | |
import { Actions, Effect, ofType } from '@ngrx/effects'; | |
import { Observable, of } from 'rxjs'; | |
import { catchError, debounceTime, map, switchMap, tap } from 'rxjs/operators'; | |
import { AuthService } from '../../services/auth.service'; | |
import { User } from '../../models/user'; | |
import { | |
LOGIN, | |
LOGIN_SUCCESS, | |
LOGIN_FAILURE, | |
LOGIN_REDIRECT, | |
REGISTER, | |
REGISTER_SUCCESS, | |
REGISTER_FAILURE, | |
LOGOUT, | |
GET_STATUS, | |
Login, | |
LoginSuccess, | |
LoginFailure, | |
Register, | |
RegisterSuccess, | |
RegisterFailure, | |
Logout, | |
GetStatus | |
} from '../actions/auth.actions'; | |
/** | |
* @class AuthEffects | |
*/ | |
@Injectable() | |
export class AuthEffects { | |
/** | |
* @constructor | |
* @param { Actions } actions | |
* @param { AuthService } authService | |
* @param { Router } router | |
* @param { HttpClient } http | |
*/ | |
constructor( | |
private actions$: Actions, | |
private authService: AuthService, | |
private router: Router, | |
private http: HttpClient | |
) {} | |
/** | |
* [Auth] Login | |
* | |
* @param { [type] } ofType(LOGIN) [description] | |
* @return Authenticated user or Error | |
*/ | |
@Effect() | |
Login: Observable<any> = this.actions$.pipe( | |
ofType(LOGIN), | |
debounceTime(500), | |
map((action: Login) => action.payload), | |
switchMap(payload => { | |
const { email, password } = payload; | |
return this.authService.login(email, password).pipe( | |
map(res => new LoginSuccess({ token: res.token, email })), | |
catchError(error => of(new LoginFailure({ error }))) | |
); | |
}) | |
); | |
/** | |
* [Auth] Login Success | |
* | |
* @param { false } dispatch Login | |
* @param { [type] } ofType(LOGIN_SUCCESS) [description] | |
*/ | |
@Effect({ dispatch: false }) | |
LoginSuccess: Observable<any> = this.actions$.pipe( | |
ofType(LOGIN_SUCCESS), | |
tap(user => { | |
const { token } = user.payload; | |
this.authService.setToken(token); | |
this.router.navigateByUrl('/'); | |
}) | |
); | |
/** | |
* [Auth] Login Failure | |
* | |
* @param { false } dispatch LoginFailure | |
* @param { [type] } ofType(LOGIN_FAILURE) [description] | |
*/ | |
@Effect({ dispatch: false }) | |
LoginFailure: Observable<any> = this.actions$.pipe( | |
ofType(LOGIN_FAILURE) | |
); | |
/** | |
* [Auth] Login Redirect | |
* | |
* @param { false } dispatch LoginRedirect | |
* @param { [type] } ofType(LOGIN_REDIRECT, LOGOUT) [description] | |
*/ | |
@Effect({ dispatch: false }) | |
LoginRedirect: Observable<any> = this.actions$.pipe( | |
ofType(LOGIN_REDIRECT, LOGOUT), | |
tap(authed => this.router.navigateByUrl('/login')) | |
); | |
/** | |
* [Auth] Register | |
* | |
* @param { false } dispatch Register | |
* @param { [type] } ofType(REGISTER) [description] | |
*/ | |
@Effect() | |
Register: Observable<any> = this.actions$.pipe( | |
ofType(REGISTER), | |
map((action: Register) => action.payload), | |
switchMap(payload => { | |
const { email, name, password } = payload; | |
return this.authService.register(email, name, password).pipe( | |
map(user => new RegisterSuccess({ email, name })), | |
catchError(error => of(new RegisterFailure({ error }))) | |
); | |
}) | |
); | |
/** | |
* [Auth] Register Success | |
* | |
* @param { false } dispatch Register Success | |
* @param { [type] } ofType(REGISTER_SUCCESS) [description] | |
*/ | |
@Effect({ dispatch: false }) | |
RegisterSuccess: Observable<any> = this.actions$.pipe( | |
ofType(REGISTER_SUCCESS), | |
tap(user => { | |
const { token } = user.payload; | |
this.authService.setToken(token); | |
this.router.navigateByUrl('/'); | |
}) | |
); | |
/** | |
* [Auth] Register Failure | |
* | |
* @param { false } dispatch Register Failure | |
* @param { [type] } ofType(REGISTER_FAILURE) [description] | |
*/ | |
@Effect({ dispatch: false }) | |
RegisterFailure: Observable<any> = this.actions$.pipe( | |
ofType(REGISTER_FAILURE) | |
); | |
/** | |
* [Auth] Logout | |
* | |
* @param { false } dispatch Logout Action | |
* @param { [type] } ofType(LOGOUT) [description] | |
*/ | |
@Effect({ dispatch: false }) | |
public Logout: Observable<any> = this.actions$.pipe( | |
ofType(LOGOUT), | |
tap(user => { | |
this.authService.removeToken(); | |
this.router.navigateByUrl('/login'); | |
}) | |
); | |
/** | |
* [Auth] GetStatus | |
* | |
* @param { false } dispatch Get Status | |
* @param { [type] } ofType(GET_STATUS) [description] | |
*/ | |
@Effect({ dispatch: false }) | |
GetStatus: Observable<any> = this.actions$.pipe( | |
ofType(GET_STATUS), | |
switchMap(payload => { | |
return this.authService.getStatus(); | |
}) | |
); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Injectable } from '@angular/core'; | |
import { CanActivate } from '@angular/router'; | |
import { Store } from '@ngrx/store'; | |
import { AuthService } from './auth.service'; | |
import { LoginRedirect } from './../store/actions/auth.actions'; | |
import { AppState } from './../store/app.states'; | |
/** | |
* Prevent unauthorized activation of routes | |
* @class AuthGuardService | |
*/ | |
@Injectable() | |
export class AuthGuardService implements CanActivate { | |
/** | |
* @constructor | |
* @param { Store } store | |
* @param { AuthService } authService | |
*/ | |
constructor( | |
private store: Store<AppState>, | |
public authService: AuthService | |
) {} | |
/** | |
* @method canActivate | |
* @return { boolean } True when user is authenticated | |
*/ | |
canActivate(): boolean { | |
if (!this.authService.getToken()) { | |
this.store.dispatch(new LoginRedirect()); | |
return false; | |
} | |
return true; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { User } from '../../models/user'; | |
import { | |
LOGIN, | |
LOGIN_SUCCESS, | |
LOGIN_FAILURE, | |
REGISTER, | |
REGISTER_SUCCESS, | |
REGISTER_FAILURE, | |
LOGOUT, | |
GET_STATUS, | |
All | |
} from '../actions/auth.actions'; | |
/** | |
* The state. | |
* @interface State | |
*/ | |
export interface State { | |
isAuthenticated: boolean; | |
user: User | null; | |
errorMessage: string | null; | |
} | |
/** | |
* The initial state. | |
*/ | |
export const initialState: State = { | |
isAuthenticated: false, | |
user: null, | |
errorMessage: null | |
} | |
/** | |
* The reducer function. | |
* @function reducer | |
* @param { State } state Current state | |
* @param { Actions } action Incoming action | |
*/ | |
export function reducer(state = initialState, action: All): State { | |
switch (action.type) { | |
case LOGIN_SUCCESS: { | |
return { | |
...state, | |
isAuthenticated: true, | |
user: { | |
token: action.payload.token, | |
email: action.payload.email | |
}, | |
errorMessage: null | |
}; | |
} | |
case LOGIN_FAILURE: { | |
return { | |
...state, | |
errorMessage: 'Incorrected email and/or password.' | |
}; | |
} | |
case REGISTER_SUCCESS: { | |
return { | |
...state, | |
isAuthenticated: true, | |
user: { | |
email: action.payload.email, | |
name: action.payload.name | |
}, | |
errorMessage: null | |
}; | |
} | |
case REGISTER_FAILURE: { | |
return { | |
...state, | |
errorMessage: 'That email is already in use.' | |
} | |
} | |
case LOGOUT: { | |
return initialState; | |
} | |
default: { | |
return state; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { HttpClient } from '@angular/common/http'; | |
import { Injectable } from '@angular/core'; | |
import { Observable } from 'rxjs'; | |
import { CookieService } from 'ngx-cookie-service'; | |
import { User } from '../models/user'; | |
/** | |
* Authentication Service | |
* | |
* @class AuthService | |
*/ | |
@Injectable() | |
export class AuthService { | |
private BASE_URL = 'http://innermind/api'; | |
/** | |
* @constructor | |
* @param { HttpClient } http | |
* @param { CookieService } cookieService | |
*/ | |
constructor(private http: HttpClient, private cookieService: CookieService) {} | |
/** | |
* Authenticated User | |
* | |
* @method Login | |
* @param { string } email The user's email address | |
* @param { string } password The user's password | |
* @return { Observable<User> } Authenticated user observable | |
*/ | |
login(email: string, password: string): Observable<any> { | |
const credentials = { email, password }; | |
const url = `${this.BASE_URL}/login`; | |
return this.http.post<User>(url, credentials); | |
} | |
/** | |
* Creates a User Account | |
* | |
* @method register | |
* @param { string } email The user's email address | |
* @param { string } name The user's name | |
* @param { string } password The user's password | |
* @return { Observable<User> } Authenticated user observable | |
*/ | |
register(email: string, name: string, password: string): Observable<User> { | |
const credentials = { email, name, password }; | |
const url = `${this.BASE_URL}/register`; | |
return this.http.post<User>(url, credentials); | |
} | |
/** | |
* @method getStatus | |
* @return { Observable<User> } [description] | |
*/ | |
getStatus(): Observable<User> { | |
const url = `${this.BASE_URL}/auth/status`; | |
return this.http.get<User>(url); | |
} | |
/** | |
* @method getToken | |
* @return { string } [description] | |
*/ | |
getToken(): string { | |
return localStorage.getItem('token'); | |
} | |
/** | |
* @method setToken | |
* @param { [type] } token [description] | |
*/ | |
setToken(token): void { | |
return localStorage.setItem('token', token); | |
} | |
/** | |
* @method removeToken | |
*/ | |
removeToken(): void { | |
return localStorage.removeItem('token'); | |
} | |
/** | |
* @method refreshToken | |
* @return { Observable<Object> } [description] | |
*/ | |
refreshToken(): Observable<Object> { | |
return this.http.get('http://innermind/api/token/refresh'); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Injectable, Injector } from '@angular/core'; | |
import { | |
HttpClient, | |
HttpEvent, | |
HttpInterceptor, | |
HttpHandler, | |
HttpRequest, | |
HttpResponse, | |
HttpErrorResponse | |
} from '@angular/common/http'; | |
import { Observable } from 'rxjs'; | |
import { catchError, tap } from 'rxjs/operators'; | |
import { Router } from '@angular/router'; | |
import { AuthService } from './auth.service'; | |
import { map } from 'rxjs/operators'; | |
/** | |
* HTTP Token Interceptor | |
* | |
* @class TokenInterceptor | |
*/ | |
@Injectable() | |
export class TokenInterceptor implements HttpInterceptor { | |
private authService: AuthService; | |
/** | |
* @constructor | |
* @param { Injector } injector | |
* @param { HttpClient } http | |
*/ | |
constructor(private injector: Injector, private http: HttpClient) {} | |
/** | |
* @method intercept | |
* @param { HttpRequest<any> } request [description] | |
* @param { HttpHandler } next [description] | |
* @return { Observable } [description] | |
*/ | |
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { | |
this.authService = this.injector.get(AuthService); | |
const token: string = this.authService.getToken(); | |
request = request.clone({ | |
setHeaders: { | |
'Authorization': `Bearer ${token}`, | |
'Content-Type': 'application/json' | |
} | |
}); | |
return next.handle(request); | |
} | |
} | |
/** | |
* HTTP Error Interceptor | |
* @class ErrorInterceptor | |
*/ | |
@Injectable() | |
export class ErrorInterceptor implements HttpInterceptor { | |
private authService: AuthService; | |
/** | |
* @constructor | |
* @param { Injector } injector | |
* @param { Router } router | |
*/ | |
constructor(private injector: Injector, private router: Router) {} | |
/** | |
* @method intercept | |
* @param { HttpRequest<any> } request [description] | |
* @param { HttpHandler } next [description] | |
* @return { Observable } [description] | |
*/ | |
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { | |
this.authService = this.injector.get(AuthService); | |
return next.handle(request).pipe( | |
catchError((response: any) => { | |
if (response instanceof HttpErrorResponse && response.status === 401) { | |
this.authService.removeToken(); | |
this.router.navigateByUrl('/login'); | |
} | |
return Observable.throw(response); | |
}) | |
); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export class User { | |
id?: string; | |
email?: string; | |
name?: string; | |
password?: string; | |
token?: string; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment