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