import { Injectable , Inject , PLATFORM_ID } from '@angular/core' ; import { isPlatformBrowser } from '@angular/common' ; import { HttpClient , HttpHeaders , HttpErrorResponse } from '@angular/common/http' ; import { Observable , ReplaySubject , BehaviorSubject } from 'rxjs' ; import { take } from 'rxjs/operators' ; import { MatSnackBar } from '@angular/material' ; import { AuthenticatedUser } from '@interfaces/authenticated-user.interface' ; import { environment } from '@environments/environment' ; interface EmailAndPassword { email : string ; password : string ; } export type UserOrError = AuthenticatedUser | HttpErrorResponse | null ; @ Injectable ( ) export class AuthService { private userSubject = new ReplaySubject < UserOrError > ( 1 ) ; private additionalProviderErrorSubject = new BehaviorSubject < HttpErrorResponse > ( null ) ; user$ : Observable < UserOrError > = this . userSubject . asObservable ( ) ; additionalProviderError$ : Observable < HttpErrorResponse > = this . additionalProviderErrorSubject . asObservable ( ) ; private jsonHeaders : HttpHeaders = new HttpHeaders ( { 'Content-Type' : 'application/json' , } ) ; private jsonOptions = { headers : this . jsonHeaders , withCredentials : true } ; constructor ( private http , @ Inject ( PLATFORM_ID ) private platformId , private snackbar ) { if ( isPlatformBrowser ( this . platformId ) ) { document . cookie . includes ( 'XSRF-TOKEN' ) ? this . reauthenticate ( ) : this . userSubject . next ( null ) ; } else { this . userSubject . next ( null ) ; } } private reauthenticate ( ) { this . http . post < AuthenticatedUser > ( `${ environment . baseUrl }/api/auth/reauthenticate` , { } , this . jsonOptions ) . pipe ( take ( 1 ) ) . subscribe ( user => this . userSubject . next ( user ) , error => this . userSubject . next ( null ) ) ; } createAnonymousUser ( ) { this . http . post < AuthenticatedUser > ( `${ environment . baseUrl }/api/auth/create-anonymous-user` , { } , this . jsonOptions ) . pipe ( take ( 1 ) ) . subscribe ( user => this . userSubject . next ( user ) , error => this . userSubject . next ( null ) ) ; } createEmailAndPasswordUserOrUpgradeAnonymousToEmailAndPassword ( { email , password } ) { this . user$ . pipe ( take ( 1 ) ) . subscribe ( user => { if ( user === null ) { return this . createEmailAndPasswordUser ( { email , password } ) ; } else if ( this . isAuthenticatedUser ( user ) && user . isAnonymous ) { return this . upgradeAnonymousUserToEmailAndPasswordUser ( { email , password } ) ; } } ) ; } private upgradeAnonymousUserToEmailAndPasswordUser ( { email , password } ) { this . http . patch < AuthenticatedUser > ( `${ environment . baseUrl }/api/auth/upgrade-anonymous-user-to-email-and-password` , { email , password } , this . jsonOptions ) . pipe ( take ( 1 ) ) . subscribe ( user => this . userSubject . next ( user ) , error => this . assignErrorToUserSubject ( error ) ) ; } private createEmailAndPasswordUser ( { email , password } ) { this . http . post < AuthenticatedUser > ( `${ environment . baseUrl }/api/auth/create-email-and-password-user` , { email , password } , this . jsonOptions ) . pipe ( take ( 1 ) ) . subscribe ( user => this . userSubject . next ( user ) , error => this . assignErrorToUserSubject ( error ) ) ; } loginWithEmailAndPassword ( { email , password } ) { this . http . post < AuthenticatedUser > ( `${ environment . baseUrl }/api/auth/login-email-and-password-user` , { email , password } , this . jsonOptions ) . pipe ( take ( 1 ) ) . subscribe ( user => this . userSubject . next ( user ) , error => this . assignErrorToUserSubject ( error ) ) ; } requestPasswordReset ( { email } ) { this . http . post ( `${ environment . baseUrl }/api/auth/request-password-reset` , { email } , this . jsonOptions ) . pipe ( take ( 1 ) ) . subscribe ( ( ) => this . snackbar . open ( `Password Reset Requested` , `OK` , { duration : 20000 , } ) , error => this . assignErrorToUserSubject ( error ) ) ; } resetPassword ( { password , token } ) { this . http . post < AuthenticatedUser > ( `${ environment . baseUrl }/api/auth/reset-password` , { password , token } , this . jsonOptions ) . pipe ( take ( 1 ) ) . subscribe ( user => this . userSubject . next ( user ) , error => this . assignErrorToUserSubject ( error ) ) ; } changePassword ( { oldPassword , newPassword } ) { return this . http . post ( `${ environment . baseUrl }/api/auth/change-password` , { oldPassword , newPassword } , this . jsonOptions ) ; } disavowAllRefreshTokens ( ) { this . http . post ( `${ environment . baseUrl }/api/auth/disavow-all-refresh-tokens` , { } , this . jsonOptions ) . pipe ( take ( 1 ) ) . subscribe ( ( ) => this . userSubject . next ( null ) , error => console . log ( error ) ) ; } logout ( ) { this . http . post ( `${ environment . baseUrl }/api/auth/logout` , { } , this . jsonOptions ) . pipe ( take ( 1 ) ) . subscribe ( ( ) => this . userSubject . next ( null ) , error => console . log ( error ) ) ; } deleteAccount ( ) { this . http . post ( `${ environment . baseUrl }/api/auth/delete-account` , { } , this . jsonOptions ) . pipe ( take ( 1 ) ) . subscribe ( ( ) => this . userSubject . next ( null ) , error => console . log ( error ) ) ; } signInWithSocialUser ( socialInfo ) { this . user$ . pipe ( take ( 1 ) ) . subscribe ( user => { if ( user === null ) { return this . authenticateSocialUser ( socialInfo ) ; } else if ( this . isAuthenticatedUser ( user ) && user . isAnonymous ) { return this . upgradeAnonymousUserToSocial ( socialInfo ) ; } else if ( this . isAuthenticatedUser ( user ) ) { return this . linkProviderToAccount ( socialInfo ) ; } } ) ; } authenticateSocialUser ( socialInfo ) { this . http . post < AuthenticatedUser > ( `${ environment . baseUrl }/api/auth/authenticate-social-user` , { ... socialInfo , } , this . jsonOptions ) . pipe ( take ( 1 ) ) . subscribe ( user => this . userSubject . next ( user ) , error => this . assignErrorToUserSubject ( error ) ) ; } upgradeAnonymousUserToSocial ( socialInfo ) { this . http . patch < AuthenticatedUser > ( `${ environment . baseUrl }/api/auth/upgrade-anonymous-user-to-social` , { ... socialInfo , } , this . jsonOptions ) . pipe ( take ( 1 ) ) . subscribe ( user => this . userSubject . next ( user ) , error => this . assignErrorToUserSubject ( error ) ) ; } linkProviderToAccount ( providerInfo ) { this . http . patch < AuthenticatedUser > ( `${ environment . baseUrl }/api/auth/link-provider-to-account` , { ... providerInfo , } , this . jsonOptions ) . pipe ( take ( 1 ) ) . subscribe ( user => this . userSubject . next ( user ) , error => this . additionalProviderErrorSubject . next ( error ) ) ; } errorHandled ( ) { this . userSubject . next ( null ) ; } additionalProviderErrorHandled ( ) { this . additionalProviderErrorSubject . next ( null ) ; } private assignErrorToUserSubject ( error ) { this . userSubject . next ( error ) ; } isAuthenticatedUser ( user ) { return ( < AuthenticatedUser > user ) . id !== undefined ; } isHttpErrorResponse ( user ) { return ( < HttpErrorResponse > user ) . error !== undefined ; } }