Link
Embed
Share
Beginning
This slide
Copy link URL
Copy link URL
Copy iframe embed code
Copy iframe embed code
Copy javascript embed code
Copy javascript embed code
Share
Tweet
Share
Tweet
Slide 1
Slide 1 text
Demystifying Token Authentication in NgRx
Slide 2
Slide 2 text
@samjulien
Slide 3
Slide 3 text
"Great, but how do I log in?” @samjulien
Slide 4
Slide 4 text
No content
Slide 5
Slide 5 text
No content
Slide 6
Slide 6 text
Authentication is a Big Scary Subject. @samjulien
Slide 7
Slide 7 text
…with lots of jargon. @samjulien
Slide 8
Slide 8 text
…with lots of jargon vocab. @samjulien
Slide 9
Slide 9 text
NgRx is a Big Scary Subject. @samjulien
Slide 10
Slide 10 text
…with lots of jargon. @samjulien
Slide 11
Slide 11 text
…with lots of jargon vocab. @samjulien
Slide 12
Slide 12 text
No content
Slide 13
Slide 13 text
Auth in NgRx looks different than auth in vanilla Angular. @samjulien
Slide 14
Slide 14 text
Feature development in NgRx looks different than feature development in vanilla Angular. @samjulien
Slide 15
Slide 15 text
@samjulien Auth in NgRx requires a different mental model than auth in vanilla Angular.
Slide 16
Slide 16 text
@samjulien Sam Julien @samjulien @samjulien
Slide 17
Slide 17 text
@samjulien Sam Julien @samjulien Developer Advocate Engineer at Auth0 @samjulien
Slide 18
Slide 18 text
@samjulien Sam Julien @samjulien Developer Advocate Engineer at Auth0 GDE & Angular Collaborator @samjulien
Slide 19
Slide 19 text
@samjulien Sam Julien @samjulien Developer Advocate Engineer at Auth0 GDE & Angular Collaborator UpgradingAngularJS.com, Thinkster, & Egghead @samjulien
Slide 20
Slide 20 text
Auth in NgRx looks different than auth in vanilla Angular. @samjulien
Slide 21
Slide 21 text
Feature development in NgRx looks different than feature development in vanilla Angular. @samjulien
Slide 22
Slide 22 text
No content
Slide 23
Slide 23 text
@samjulien Auth in NgRx requires a different mental model than auth in vanilla Angular.
Slide 24
Slide 24 text
No content
Slide 25
Slide 25 text
Start login Handle redirect Log in to provider @samjulien
Slide 26
Slide 26 text
Handle redirect Success Error @samjulien
Slide 27
Slide 27 text
@samjulien
Slide 28
Slide 28 text
Auth Service @samjulien
Slide 29
Slide 29 text
Auth Service Components @samjulien
Slide 30
Slide 30 text
Auth Service Components Data Services @samjulien
Slide 31
Slide 31 text
@samjulien
Slide 32
Slide 32 text
Auth Service Components Data Services @samjulien
Slide 33
Slide 33 text
Reducers Components Effects Auth Service @samjulien
Slide 34
Slide 34 text
Auth Service Components Data Services @samjulien
Slide 35
Slide 35 text
Reducers Components Effects Auth Service @samjulien
Slide 36
Slide 36 text
No content
Slide 37
Slide 37 text
No content
Slide 38
Slide 38 text
No content
Slide 39
Slide 39 text
No content
Slide 40
Slide 40 text
State Side Effects @samjulien
Slide 41
Slide 41 text
State Side Effects @samjulien
Slide 42
Slide 42 text
State What do I need to keep track of? Side Effects @samjulien
Slide 43
Slide 43 text
State What do I need to keep track of? Side Effects What events don’t directly change state? @samjulien
Slide 44
Slide 44 text
State Side Effects @samjulien
Slide 45
Slide 45 text
State Video Game Collection Side Effects @samjulien
Slide 46
Slide 46 text
State Video Game Collection Side Effects Call the API to get the collection. @samjulien
Slide 47
Slide 47 text
State Side Effects @samjulien
Slide 48
Slide 48 text
State Game Ownership Side Effects @samjulien
Slide 49
Slide 49 text
State Game Ownership Side Effects Call the API to add to the collection. @samjulien
Slide 50
Slide 50 text
State @samjulien
Slide 51
Slide 51 text
What do I need to keep track of? @samjulien
Slide 52
Slide 52 text
Where do I keep it? @samjulien
Slide 53
Slide 53 text
The Store
Slide 54
Slide 54 text
What goes in the store? @samjulien
Slide 55
Slide 55 text
Start login Handle redirect Log in to provider @samjulien
Slide 56
Slide 56 text
Handle redirect Success Error @samjulien
Slide 57
Slide 57 text
Success @samjulien
Slide 58
Slide 58 text
Success User Token Authenticated @samjulien
Slide 59
Slide 59 text
Success User Token Authenticated Redirect @samjulien
Slide 60
Slide 60 text
@samjulien
Slide 61
Slide 61 text
Auth Service @samjulien
Slide 62
Slide 62 text
export class AuthService { isAuthenticated: boolean = null; private userProfileSubject$ = new BehaviorSubject(null); userProfile$ = this.userProfileSubject$.asObservable(); private tokenSubject$ = new BehaviorSubject(null); accessToken$ = this.userProfileSubject$.asObservable(); } @samjulien
Slide 63
Slide 63 text
export class AuthService { isAuthenticated: boolean = null; private userProfileSubject$ = new BehaviorSubject(null); userProfile$ = this.userProfileSubject$.asObservable(); private tokenSubject$ = new BehaviorSubject(null); accessToken$ = this.userProfileSubject$.asObservable(); } @samjulien
Slide 64
Slide 64 text
export class AuthService { isAuthenticated: boolean = null; private userProfileSubject$ = new BehaviorSubject(null); userProfile$ = this.userProfileSubject$.asObservable(); private tokenSubject$ = new BehaviorSubject(null); accessToken$ = this.userProfileSubject$.asObservable(); } @samjulien
Slide 65
Slide 65 text
export class AuthService { isAuthenticated: boolean = null; private userProfileSubject$ = new BehaviorSubject(null); userProfile$ = this.userProfileSubject$.asObservable(); private tokenSubject$ = new BehaviorSubject(null); accessToken$ = this.userProfileSubject$.asObservable(); } @samjulien
Slide 66
Slide 66 text
export class AuthService { isAuthenticated: boolean = null; private userProfileSubject$ = new BehaviorSubject(null); userProfile$ = this.userProfileSubject$.asObservable(); private tokenSubject$ = new BehaviorSubject(null); accessToken$ = this.userProfileSubject$.asObservable(); } @samjulien
Slide 67
Slide 67 text
export class AuthService { isAuthenticated: boolean = null; private userProfileSubject$ = new BehaviorSubject(null); userProfile$ = this.userProfileSubject$.asObservable(); private tokenSubject$ = new BehaviorSubject(null); accessToken$ = this.userProfileSubject$.asObservable(); } @samjulien
Slide 68
Slide 68 text
@samjulien
Slide 69
Slide 69 text
export interface State { isAuthenticated: boolean; userProfile: UserProfile; accessToken: AccessToken; } @samjulien
Slide 70
Slide 70 text
export interface State { isAuthenticated: boolean; userProfile: UserProfile; accessToken: AccessToken; } @samjulien
Slide 71
Slide 71 text
export interface State { isAuthenticated: boolean; userProfile: UserProfile; accessToken: AccessToken; } @samjulien
Slide 72
Slide 72 text
export interface State { isAuthenticated: boolean; userProfile: UserProfile; accessToken: AccessToken; } @samjulien
Slide 73
Slide 73 text
Don’t keep access tokens in local storage! @samjulien
Slide 74
Slide 74 text
export interface State { isAuthenticated: boolean; userProfile: UserProfile; accessToken: AccessToken; } @samjulien
Slide 75
Slide 75 text
What messages do we need about state? @samjulien
Slide 76
Slide 76 text
What actions do we need? @samjulien
Slide 77
Slide 77 text
Actions
Slide 78
Slide 78 text
@samjulien
Slide 79
Slide 79 text
@samjulien
Slide 80
Slide 80 text
Events State Changes @samjulien
Slide 81
Slide 81 text
Events @samjulien
Slide 82
Slide 82 text
Start login Handle redirect Log in to provider @samjulien
Slide 83
Slide 83 text
Handle redirect Success Error @samjulien
Slide 84
Slide 84 text
Success @samjulien
Slide 85
Slide 85 text
Success User Token Authenticated @samjulien
Slide 86
Slide 86 text
Success User Token Authenticated Redirect @samjulien
Slide 87
Slide 87 text
export const logIn = createAction( ‘[Auth] Start Log In’ ); @samjulien export const logOut = createAction( '[Auth] Log out' );
Slide 88
Slide 88 text
export const handleRedirect = createAction( ‘[Auth] Handle redirect’ ); @samjulien
Slide 89
Slide 89 text
export const handleRedirectSuccess = createAction( '[Auth] Handle redirect success', props<{ targetRoute: string }>() ); @samjulien export const handleRedirectError = createAction( '[Auth] Handle redirect error’, props<{ error: string }>() );
Slide 90
Slide 90 text
Events State Changes @samjulien
Slide 91
Slide 91 text
State Changes @samjulien
Slide 92
Slide 92 text
Start login Handle redirect Log in to provider @samjulien
Slide 93
Slide 93 text
Handle redirect Success Error @samjulien
Slide 94
Slide 94 text
Success @samjulien
Slide 95
Slide 95 text
Success User Token Authenticated @samjulien
Slide 96
Slide 96 text
Success User Token Authenticated Redirect @samjulien
Slide 97
Slide 97 text
export interface State { isAuthenticated: boolean; userProfile: UserProfile; accessToken: AccessToken; } @samjulien
Slide 98
Slide 98 text
export const loadUser = createAction( '[Auth] Load user’ ); export const loadUserSuccess = createAction( '[Auth] Load user success', props<{ user: User }>() ); @samjulien
Slide 99
Slide 99 text
export const loadUser = createAction( '[Auth] Load user’ ); export const loadUserSuccess = createAction( '[Auth] Load user success', props<{ user: User }>() ); @samjulien
Slide 100
Slide 100 text
export const loadUser = createAction( '[Auth] Load user’ ); export const loadUserSuccess = createAction( '[Auth] Load user success', props<{ user: User }>() ); @samjulien
Slide 101
Slide 101 text
export const checkAuth = createAction('[Auth] Check auth’); export const checkAuthSuccess = createAction( '[Auth] Check auth success', props<{ isAuthenticated: boolean }>() ); export const setNotAuthenticated = createAction( '[Auth] Not authenticated', props<{ isAuthenticated: boolean }>() ); @samjulien
Slide 102
Slide 102 text
export const checkAuth = createAction('[Auth] Check auth’); export const checkAuthSuccess = createAction( '[Auth] Check auth success', props<{ isAuthenticated: boolean }>() ); export const setNotAuthenticated = createAction( '[Auth] Not authenticated', props<{ isAuthenticated: boolean }>() ); @samjulien
Slide 103
Slide 103 text
export const checkAuth = createAction('[Auth] Check auth’); export const checkAuthSuccess = createAction( '[Auth] Check auth success', props<{ isAuthenticated: boolean }>() ); export const setNotAuthenticated = createAction( '[Auth] Not authenticated', props<{ isAuthenticated: boolean }>() ); @samjulien
Slide 104
Slide 104 text
export const checkAuth = createAction('[Auth] Check auth’); export const checkAuthSuccess = createAction( '[Auth] Check auth success', props<{ isAuthenticated: boolean }>() ); export const setNotAuthenticated = createAction( '[Auth] Not authenticated', props<{ isAuthenticated: boolean }>() ); @samjulien
Slide 105
Slide 105 text
export const getToken = createAction('[Auth] Get token’); export const getTokenSuccess = createAction( '[Auth] Get token success', props<{ accessToken: Token }>() ); export const getTokenFailure = createAction( '[Auth] Get token failure', props<{ error: string }>() ); @samjulien
Slide 106
Slide 106 text
export const getToken = createAction('[Auth] Get token’); export const getTokenSuccess = createAction( '[Auth] Get token success', props<{ accessToken: Token }>() ); export const getTokenFailure = createAction( '[Auth] Get token failure', props<{ error: string }>() ); @samjulien
Slide 107
Slide 107 text
export const getToken = createAction('[Auth] Get token’); export const getTokenSuccess = createAction( '[Auth] Get token success', props<{ accessToken: Token }>() ); export const getTokenFailure = createAction( '[Auth] Get token failure', props<{ error: string }>() ); @samjulien
Slide 108
Slide 108 text
export const getToken = createAction('[Auth] Get token’); export const getTokenSuccess = createAction( '[Auth] Get token success', props<{ accessToken: Token }>() ); export const getTokenFailure = createAction( '[Auth] Get token failure', props<{ error: string }>() ); @samjulien
Slide 109
Slide 109 text
Where does state change? @samjulien
Slide 110
Slide 110 text
Reducers
Slide 111
Slide 111 text
Defining Reducers @samjulien
Slide 112
Slide 112 text
export const reducer = createReducer( initialState, // on()... ); @samjulien
Slide 113
Slide 113 text
on( AuthActions.checkAuthSuccess, AuthActions.setNotAuthenticated, (state, { isAuthenticated }) => { return { ...state, isAuthenticated, }; } ) @samjulien
Slide 114
Slide 114 text
on( AuthActions.loadUserSuccess, (state, { user }) => { return { ...state, user, }; }), @samjulien
Slide 115
Slide 115 text
on( AuthActions.getTokenSuccess, (state, { accessToken }) => { return { ...state, accessToken, }; } ) @samjulien
Slide 116
Slide 116 text
How do I read state in components? @samjulien
Slide 117
Slide 117 text
Selectors
Slide 118
Slide 118 text
@samjulien
Slide 119
Slide 119 text
Auth Service Components @samjulien
Slide 120
Slide 120 text
@samjulien
Slide 121
Slide 121 text
Reducers Components Effects Auth Service @samjulien
Slide 122
Slide 122 text
Reducers Components Effects Auth Service @samjulien Selectors
Slide 123
Slide 123 text
Defining Selectors @samjulien
Slide 124
Slide 124 text
@samjulien export const selectUser = (state: State) => state.user; export const selectIsAuthenticated = (state: State) => state.isAuthenticated; export const selectAccessToken = (state: State) => state.accessToken;
Slide 125
Slide 125 text
@samjulien export const selectUser = (state: State) => state.user; export const selectIsAuthenticated = (state: State) => state.isAuthenticated; export const selectAccessToken = (state: State) => state.accessToken;
Slide 126
Slide 126 text
@samjulien export const selectUser = (state: State) => state.user; export const selectIsAuthenticated = (state: State) => state.isAuthenticated; export const selectAccessToken = (state: State) => state.accessToken;
Slide 127
Slide 127 text
@samjulien export const selectUser = (state: State) => state.user; export const selectIsAuthenticated = (state: State) => state.isAuthenticated; export const selectAccessToken = (state: State) => state.accessToken;
Slide 128
Slide 128 text
@samjulien export const selectUser = createSelector( selectAuthStatus, fromAuthStatus.selectUser ); export const selectAccessToken = createSelector( selectAuthStatus, fromAuthStatus.selectAccessToken );
Slide 129
Slide 129 text
@samjulien export const selectUser = createSelector( selectAuthStatus, fromAuthStatus.selectUser ); export const selectAccessToken = createSelector( selectAuthStatus, fromAuthStatus.selectAccessToken );
Slide 130
Slide 130 text
@samjulien export const selectUser = createSelector( selectAuthStatus, fromAuthStatus.selectUser ); export const selectAccessToken = createSelector( selectAuthStatus, fromAuthStatus.selectAccessToken );
Slide 131
Slide 131 text
@samjulien export const selectIsAuthenticated = createSelector( selectAuthStatus, fromAuthStatus.selectIsAuthenticated );
Slide 132
Slide 132 text
Okay, but what about the auth calls? @samjulien
Slide 133
Slide 133 text
Auth Service
Slide 134
Slide 134 text
State Side Effects @samjulien
Slide 135
Slide 135 text
No content
Slide 136
Slide 136 text
Reducers Components Effects Auth Service @samjulien
Slide 137
Slide 137 text
@samjulien
Slide 138
Slide 138 text
export class AuthService { handleRedirectCallback$ = from(this.authClient.handleRedirectCallback()); } @samjulien
Slide 139
Slide 139 text
export class AuthService {⠀ handleRedirect() {⠀ }⠀ }⠀ @samjulien
Slide 140
Slide 140 text
export class AuthService { handleRedirect() { if (weHaveACode) { let targetRoute: string; const authComplete$ = this.handleRedirectCallback$.pipe( tap(response => { targetRoute = this.processUrl(response); }), concatMap(() => { return combineLatest([ this.getUser$(), this.isAuthenticated$ ]); }) ); authComplete$.subscribe(([user, loggedIn]) => { this.router.navigate([targetRoute]); }); } } } @samjulien
Slide 141
Slide 141 text
export class AuthService { handleRedirect() { if (weHaveACode) { let targetRoute: string; const authComplete$ = this.handleRedirectCallback$.pipe( tap(response => { targetRoute = this.processUrl(response); }), concatMap(() => { return combineLatest([ this.getUser$(), this.isAuthenticated$ ]); }) ); authComplete$.subscribe(([user, loggedIn]) => { this.router.navigate([targetRoute]); }); } } } @samjulien
Slide 142
Slide 142 text
export class AuthService { handleRedirect() { if (weHaveACode) { let targetRoute: string; const authComplete$ = this.handleRedirectCallback$.pipe( tap(response => { targetRoute = this.processUrl(response); }), concatMap(() => { return combineLatest([ this.getUser$(), this.isAuthenticated$ ]); }) ); authComplete$.subscribe(([user, loggedIn]) => { this.router.navigate([targetRoute]); }); } } } @samjulien
Slide 143
Slide 143 text
export class AuthService { handleRedirect() { if (weHaveACode) { let targetRoute: string; const authComplete$ = this.handleRedirectCallback$.pipe( tap(response => { targetRoute = this.processUrl(response); }), concatMap(() => { return combineLatest([ this.getUser$(), this.isAuthenticated$ ]); }) ); authComplete$.subscribe(([user, loggedIn]) => { this.router.navigate([targetRoute]); }); } } } @samjulien
Slide 144
Slide 144 text
export class AuthService { handleRedirect() { if (weHaveACode) { let targetRoute: string; const authComplete$ = this.handleRedirectCallback$.pipe( tap(response => { targetRoute = this.processUrl(response); }), concatMap(() => { return combineLatest([ this.getUser$(), this.isAuthenticated$ ]); }) ); authComplete$.subscribe(([user, loggedIn]) => { this.router.navigate([targetRoute]); }); } } } @samjulien
Slide 145
Slide 145 text
export class AuthService { handleRedirect() { if (weHaveACode) { let targetRoute: string; const authComplete$ = this.handleRedirectCallback$.pipe( tap(response => { targetRoute = this.processUrl(response); }), concatMap(() => { return combineLatest([ this.getUser$(), this.isAuthenticated$ ]); }) ); authComplete$.subscribe(([user, loggedIn]) => { this.router.navigate([targetRoute]); }); } } } @samjulien
Slide 146
Slide 146 text
export class AuthService { handleRedirect() { if (weHaveACode) { let targetRoute: string; const authComplete$ = this.handleRedirectCallback$.pipe( tap(response => { targetRoute = this.processUrl(response); }), concatMap(() => { return combineLatest([ this.getUser$(), this.isAuthenticated$ ]); }) ); authComplete$.subscribe(([user, loggedIn]) => { this.router.navigate([targetRoute]); }); } } } @samjulien
Slide 147
Slide 147 text
export class AuthService { handleRedirect() { if (weHaveACode) { let targetRoute: string; const authComplete$ = this.handleRedirectCallback$.pipe( tap(response => { targetRoute = this.processUrl(response); }), concatMap(() => { return combineLatest([ this.getUser$(), this.isAuthenticated$ ]); }) ); authComplete$.subscribe(([user, loggedIn]) => { this.router.navigate([targetRoute]); }); } } } @samjulien
Slide 148
Slide 148 text
@samjulien
Slide 149
Slide 149 text
export class AuthService { handleRedirectCallback$ = from(this.authClient.handleRedirectCallback()); } @samjulien
Slide 150
Slide 150 text
export class AuthService { handleRedirectCallback$ = from(this.authClient.handleRedirectCallback()); getUser$(options) { return from(this.authClient.getUser(options)); } login() { this.authClient.loginWithRedirect(); } logout() { this.authClient$.logout(); } } @samjulien
Slide 151
Slide 151 text
export class AuthService { handleRedirectCallback$ = from(this.authClient.handleRedirectCallback()); getUser$(options) { return from(this.authClient.getUser(options)); } login() { this.authClient.loginWithRedirect(); } logout() { this.authClient$.logout(); } } @samjulien
Slide 152
Slide 152 text
export class AuthService { handleRedirectCallback$ = from(this.authClient.handleRedirectCallback()); getUser$(options) { return from(this.authClient.getUser(options)); } login() { this.authClient.loginWithRedirect(); } logout() { this.authClient$.logout(); } } @samjulien
Slide 153
Slide 153 text
export class AuthService { handleRedirectCallback$ = from(this.authClient.handleRedirectCallback()); getUser$(options) { return from(this.authClient.getUser(options)); } login() { this.authClient.loginWithRedirect(); } logout() { this.authClient$.logout(); } } @samjulien
Slide 154
Slide 154 text
export class AuthService { handleRedirectCallback$ = from(this.authClient.handleRedirectCallback()); getUser$(options) { return from(this.authClient.getUser(options)); } login() { this.authClient.loginWithRedirect(); } logout() { this.authClient$.logout(); } } @samjulien
Slide 155
Slide 155 text
Authentication service will be very thin. @samjulien
Slide 156
Slide 156 text
No content
Slide 157
Slide 157 text
State Side Effects @samjulien
Slide 158
Slide 158 text
Side Effects @samjulien
Slide 159
Slide 159 text
How do we handle events don’t directly change state? @samjulien
Slide 160
Slide 160 text
Effects
Slide 161
Slide 161 text
Start login Handle redirect Log in to provider @samjulien
Slide 162
Slide 162 text
Handle redirect Success Error @samjulien
Slide 163
Slide 163 text
Success @samjulien
Slide 164
Slide 164 text
Success User Token Authenticated @samjulien
Slide 165
Slide 165 text
Success User Token Authenticated Redirect @samjulien
Slide 166
Slide 166 text
login$ = createEffect( () => this.actions$.pipe( ofType(AuthActions.login), tap(() => this.authService.login()) ), { dispatch: false } ); @samjulien
Slide 167
Slide 167 text
login$ = createEffect( () => this.actions$.pipe( ofType(AuthActions.login), tap(() => this.authService.login()) ), { dispatch: false } ); @samjulien
Slide 168
Slide 168 text
login$ = createEffect( () => this.actions$.pipe( ofType(AuthActions.login), tap(() => this.authService.login()) ), { dispatch: false } ); @samjulien
Slide 169
Slide 169 text
login$ = createEffect( () => this.actions$.pipe( ofType(AuthActions.login), tap(() => this.authService.login()) ), { dispatch: false } ); @samjulien
Slide 170
Slide 170 text
handleRedirect$ = createEffect(() => this.actions$.pipe( ofType(AuthActions.handleRedirect), exhaustMap(() => { @samjulien ) ); ... })
Slide 171
Slide 171 text
handleRedirect$ = createEffect(() => this.actions$.pipe( ofType(AuthActions.handleRedirect), exhaustMap(() => { @samjulien ) ); ... })
Slide 172
Slide 172 text
handleRedirect$ = createEffect(() => this.actions$.pipe( ofType(AuthActions.handleRedirect), exhaustMap(() => { @samjulien ) ); ... })
Slide 173
Slide 173 text
handleRedirect$ = createEffect(() => this.actions$.pipe( ofType(AuthActions.handleRedirect), exhaustMap(() => { @samjulien ) ); ... })
Slide 174
Slide 174 text
exhaustMap(() => { @samjulien ...
Slide 175
Slide 175 text
exhaustMap(() => { @samjulien // handle redirect and process tokens this.authService.handleRedirect.pipe( map(({ redirectUrl }) => AuthActions.handleRedirectSuccess( { redirectUrl } )), catchError(({ error }) => AuthActions.handleRedirectFailure( { error } )) );
Slide 176
Slide 176 text
exhaustMap(() => { @samjulien // handle redirect and process tokens this.authService.handleRedirect.pipe( map(({ redirectUrl }) => AuthActions.handleRedirectSuccess( { redirectUrl } )), catchError(({ error }) => AuthActions.handleRedirectFailure( { error } )) );
Slide 177
Slide 177 text
exhaustMap(() => { @samjulien // handle redirect and process tokens this.authService.handleRedirect.pipe( map(({ redirectUrl }) => AuthActions.handleRedirectSuccess( { redirectUrl } )), catchError(({ error }) => AuthActions.handleRedirectFailure( { error } )) );
Slide 178
Slide 178 text
exhaustMap(() => { @samjulien // handle redirect and process tokens this.authService.handleRedirect.pipe( map(({ redirectUrl }) => AuthActions.handleRedirectSuccess( { redirectUrl } )), catchError(({ error }) => AuthActions.handleRedirectFailure( { error } )) );
Slide 179
Slide 179 text
exhaustMap(() => { @samjulien // handle redirect and process tokens this.authService.handleRedirect.pipe( map(({ redirectUrl }) => AuthActions.handleRedirectSuccess( { redirectUrl } )), catchError(({ error }) => AuthActions.handleRedirectFailure( { error } )) );
Slide 180
Slide 180 text
exhaustMap(() => { @samjulien // handle redirect and process tokens this.authService.handleRedirect.pipe( map(({ redirectUrl }) => AuthActions.handleRedirectSuccess( { redirectUrl } )), catchError(({ error }) => AuthActions.handleRedirectFailure( { error } )) );
Slide 181
Slide 181 text
handleRedirectSuccess$ = createEffect( () => this.actions$.pipe( ofType(AuthActions.handleRedirectSucess), tap(({ redirectUrl }) => this.router.navigate([redirectUrl])) ), { dispatch: false } ); @samjulien
Slide 182
Slide 182 text
handleRedirectSuccess$ = createEffect( () => this.actions$.pipe( ofType(AuthActions.handleRedirectSucess), tap(({ redirectUrl }) => this.router.navigate([redirectUrl])) ), { dispatch: false } ); @samjulien
Slide 183
Slide 183 text
handleRedirectSuccess$ = createEffect( () => this.actions$.pipe( ofType(AuthActions.handleRedirectSucess), tap(({ redirectUrl }) => this.router.navigate([redirectUrl])) ), { dispatch: false } ); @samjulien
Slide 184
Slide 184 text
handleRedirectSuccess$ = createEffect( () => this.actions$.pipe( ofType(AuthActions.handleRedirectSucess), tap(({ redirectUrl }) => this.router.navigate([redirectUrl])) ), { dispatch: false } ); @samjulien
Slide 185
Slide 185 text
checkAuth$ = createEffect(() => this.actions$.pipe( ofType(AuthActions.checkAuth, AuthActions.handleRedirectSuccess), concatMap(() => this.authService.isAuthenticated$.pipe( map(isAuthenticated => isAuthenticated ? AuthActions.checkAuthSuccess({ isAuthenticated }) : AuthActions.setNotAuthenticated({ isAuthenticated }) ) ) ) ) ); @samjulien
Slide 186
Slide 186 text
checkAuth$ = createEffect(() => this.actions$.pipe( ofType(AuthActions.checkAuth, AuthActions.handleRedirectSuccess), concatMap(() => this.authService.isAuthenticated$.pipe( map(isAuthenticated => isAuthenticated ? AuthActions.checkAuthSuccess({ isAuthenticated }) : AuthActions.setNotAuthenticated({ isAuthenticated }) ) ) ) ) ); @samjulien
Slide 187
Slide 187 text
checkAuth$ = createEffect(() => this.actions$.pipe( ofType(AuthActions.checkAuth, AuthActions.handleRedirectSuccess), concatMap(() => this.authService.isAuthenticated$.pipe( map(isAuthenticated => isAuthenticated ? AuthActions.checkAuthSuccess({ isAuthenticated }) : AuthActions.setNotAuthenticated({ isAuthenticated }) ) ) ) ) ); @samjulien
Slide 188
Slide 188 text
checkAuth$ = createEffect(() => this.actions$.pipe( ofType(AuthActions.checkAuth, AuthActions.handleRedirectSuccess), concatMap(() => this.authService.isAuthenticated$.pipe( map(isAuthenticated => isAuthenticated ? AuthActions.checkAuthSuccess({ isAuthenticated }) : AuthActions.setNotAuthenticated({ isAuthenticated }) ) ) ) ) ); @samjulien
Slide 189
Slide 189 text
checkAuth$ = createEffect(() => this.actions$.pipe( ofType(AuthActions.checkAuth, AuthActions.handleRedirectSuccess), concatMap(() => this.authService.isAuthenticated$.pipe( map(isAuthenticated => isAuthenticated ? AuthActions.checkAuthSuccess({ isAuthenticated }) : AuthActions.setNotAuthenticated({ isAuthenticated }) ) ) ) ) ); @samjulien
Slide 190
Slide 190 text
checkAuth$ = createEffect(() => this.actions$.pipe( ofType(AuthActions.checkAuth, AuthActions.handleRedirectSuccess), concatMap(() => this.authService.isAuthenticated$.pipe( map(isAuthenticated => isAuthenticated ? AuthActions.checkAuthSuccess({ isAuthenticated }) : AuthActions.setNotAuthenticated({ isAuthenticated }) ) ) ) ) ); @samjulien
Slide 191
Slide 191 text
loadUser$ = createEffect(() => this.actions$.pipe( ofType(AuthActions.loadUser, AuthActions.checkAuthSuccess), exhaustMap(() => this.authService.getUser$().pipe( map(user => { return AuthActions.loadUserSuccess({ user }); }) ) ) ) ); @samjulien
Slide 192
Slide 192 text
loadUser$ = createEffect(() => this.actions$.pipe( ofType(AuthActions.loadUser, AuthActions.checkAuthSuccess), exhaustMap(() => this.authService.getUser$().pipe( map(user => { return AuthActions.loadUserSuccess({ user }); }) ) ) ) ); @samjulien
Slide 193
Slide 193 text
loadUser$ = createEffect(() => this.actions$.pipe( ofType(AuthActions.loadUser, AuthActions.checkAuthSuccess), exhaustMap(() => this.authService.getUser$().pipe( map(user => { return AuthActions.loadUserSuccess({ user }); }) ) ) ) ); @samjulien
Slide 194
Slide 194 text
loadUser$ = createEffect(() => this.actions$.pipe( ofType(AuthActions.loadUser, AuthActions.checkAuthSuccess), exhaustMap(() => this.authService.getUser$().pipe( map(user => { return AuthActions.loadUserSuccess({ user }); }) ) ) ) ); @samjulien
Slide 195
Slide 195 text
loadUser$ = createEffect(() => this.actions$.pipe( ofType(AuthActions.loadUser, AuthActions.checkAuthSuccess), exhaustMap(() => this.authService.getUser$().pipe( map(user => { return AuthActions.loadUserSuccess({ user }); }) ) ) ) ); @samjulien
Slide 196
Slide 196 text
Success User Token Authenticated @samjulien
Slide 197
Slide 197 text
User Token Authenticated @samjulien
Slide 198
Slide 198 text
User Token Authenticated @samjulien
Slide 199
Slide 199 text
Reducers Components Effects Auth Service @samjulien
Slide 200
Slide 200 text
Effects are the brain of the authentication flow. @samjulien
Slide 201
Slide 201 text
No content
Slide 202
Slide 202 text
Let’s Review
Slide 203
Slide 203 text
Auth in NgRx looks different than auth in vanilla Angular. @samjulien
Slide 204
Slide 204 text
No content
Slide 205
Slide 205 text
No content
Slide 206
Slide 206 text
Feature development in NgRx looks different than feature development in vanilla Angular. @samjulien
Slide 207
Slide 207 text
No content
Slide 208
Slide 208 text
Start login Handle redirect Log in to provider @samjulien
Slide 209
Slide 209 text
Handle redirect Success Error @samjulien
Slide 210
Slide 210 text
@samjulien
Slide 211
Slide 211 text
Auth Service @samjulien
Slide 212
Slide 212 text
Auth Service Components Data Services @samjulien
Slide 213
Slide 213 text
@samjulien
Slide 214
Slide 214 text
State Side Effects @samjulien
Slide 215
Slide 215 text
Auth Service Components Data Services @samjulien
Slide 216
Slide 216 text
Reducers Components Effects Auth Service @samjulien
Slide 217
Slide 217 text
No content
Slide 218
Slide 218 text
No content
Slide 219
Slide 219 text
No content
Slide 220
Slide 220 text
No content
Slide 221
Slide 221 text
Reducers Components Effects Auth Service @samjulien
Slide 222
Slide 222 text
No content
Slide 223
Slide 223 text
No content
Slide 224
Slide 224 text
samj.im/ngrx-auth @samjulien
Slide 225
Slide 225 text
samj.im/ngrx-auth Thank you! @samjulien