I am using angular on the front end, django rest framework on the backend
I am using auth0 to handle authentication.
I had everything working fine and then I followed the docs on how to secure an api with a users auth0 credentials.
How I have this schema. A user logs in with auth0 then my system sees if a user is connected with that auth0id if not it prompts the user to create a user for my system.
So a auth0 user is attached to a system user
Regardless
when I login to my system using auth0 I console log the user object that is returned and that console log does contain the user object.
The user object token is stored in localstorage.
The auth0 auth.service is below. It is pretty standard from what came from the auth0 docs:
import { Injectable } from '@angular/core';
import createAuth0Client from '@auth0/auth0-spa-js';
import Auth0Client from '@auth0/auth0-spa-js/dist/typings/Auth0Client';
import { from, of, Observable, BehaviorSubject, combineLatest, throwError } from 'rxjs';
import { tap, catchError, concatMap, shareReplay } from 'rxjs/operators';
import { Router } from '@angular/router';
import {environment} from '../../environments/environment';
import {HttpClient} from '@angular/common/http';
@Injectable()
export class AuthService {
// Create an observable of Auth0 instance of client
auth0Client$ = (from(
createAuth0Client({
domain: environment.auth0_domain,
client_id: environment.auth0_client_id,
redirect_uri: `${window.location.origin}`,
useRefreshTokens: true,
cacheLocation: 'localstorage'
})
) as Observable<Auth0Client>).pipe(
shareReplay(1), // Every subscription receives the same shared value
catchError(err => throwError(err))
);
// Define observables for SDK methods that return promises by default
// For each Auth0 SDK method, first ensure the client instance is ready
// concatMap: Using the client instance, call SDK method; SDK returns a promise
// from: Convert that resulting promise into an observable
isAuthenticated$ = this.auth0Client$.pipe(
concatMap((client: Auth0Client) => from(client.isAuthenticated())),
tap(res => this.loggedIn = res)
);
handleRedirectCallback$ = this.auth0Client$.pipe(
concatMap((client: Auth0Client) => from(client.handleRedirectCallback()))
);
// Create subject and public observable of user profile data
private userProfileSubject$ = new BehaviorSubject<any>(null);
userProfile$ = this.userProfileSubject$.asObservable();
// Create a local property for login status
loggedIn: boolean = null;
constructor(private router: Router, private http: HttpClient) {
// On initial load, check authentication state with authorization server
// Set up local auth streams if user is already authenticated
this.localAuthSetup();
// Handle redirect from Auth0 login
this.handleAuthCallback();
}
// When calling, options can be passed if desired
// https://auth0.github.io/auth0-spa-js/classes/auth0client.html#getuser
getUser$(options?): Observable<any> {
return this.auth0Client$.pipe(
concatMap((client: Auth0Client) => from(client.getUser(options))),
tap(user => {
this.userProfileSubject$.next(user);
console.log(user);
})
);
}
// for when a user attempts to sign up by clicking the log in button vs clicking the sign up button
// these set of functions and class variables deal with that
rawsenduserobject(): Observable<any>{
return this.userProfile$;
}
// end functions and variables that deal with improper user sign up
private localAuthSetup() {
// This should only be called on app initialization
// Set up local authentication streams
const checkAuth$ = this.isAuthenticated$.pipe(
concatMap((loggedIn: boolean) => {
if (loggedIn) {
// If authenticated, get user and set in app
// NOTE: you could pass options here if needed
return this.getUser$();
}
// If not authenticated, return stream that emits 'false'
return of(loggedIn);
})
);
checkAuth$.subscribe();
}
login(redirectPath: string = '/') {
// A desired redirect path can be passed to login method
// (e.g., from a route guard)
// Ensure Auth0 client instance exists
this.auth0Client$.subscribe((client: Auth0Client) => {
// Call method to log in
client.loginWithRedirect({
redirect_uri: `${window.location.origin}`,
appState: { target: redirectPath }
});
});
}
private handleAuthCallback() {
// Call when app reloads after user logs in with Auth0
const params = window.location.search;
if (params.includes('code=') && params.includes('state=')) {
let targetRoute: string; // Path to redirect to after login processsed
const authComplete$ = this.handleRedirectCallback$.pipe(
// Have client, now call method to handle auth callback redirect
tap(cbRes => {
// Get and set target redirect route from callback results
targetRoute = cbRes.appState && cbRes.appState.target ? cbRes.appState.target : '/';
}),
concatMap(() => {
// Redirect callback complete; get user and login status
return combineLatest([
this.getUser$(),
this.isAuthenticated$
]);
})
);
// Subscribe to authentication completion observable
// Response will be an array of user and login status
authComplete$.subscribe(([user, loggedIn]) => {
// Redirect to target route after callback processing
this.router.navigate([targetRoute]);
});
}
}
logout() {
// Ensure Auth0 client instance exists
this.auth0Client$.subscribe((client: Auth0Client) => {
// Call method to log out
client.logout({
client_id: environment.auth0_client_id,
returnTo: `${window.location.origin}`
});
});
}
// for sending the user object from the backend
serversidebehaviorsubject = new BehaviorSubject(null);
sendserversideuser(user){
this.serversidebehaviorsubject.next(user);
}
receiveserversideuser():Observable<any>{
return this.serversidebehaviorsubject.asObservable();
}
}
the http interceptor again mostly from what was presented in the docs is below:
imports: [
BrowserModule,
RouterModule,
FontAwesomeModule,
RecaptchaV3Module,
FormsModule,
HttpClientModule,
AppRoutingModule,
HttpClientModule,
AuthModule.forRoot({
domain: environment.auth0_domain,
clientId: environment.auth0_client_id,
redirectUri: window.location.origin,
// The AuthHttpInterceptor configuration
httpInterceptor: {
allowedList: [
{
uri: 'http://127.0.0.1:8000/api/*'
}
],
},
})
],
providers: [Addemailtomarketinglistservice,
{ provide: HTTP_INTERCEPTORS, useClass: AuthHttpInterceptor, multi: true },
{provide: RECAPTCHA_V3_SITE_KEY, useValue: environment.recaptcha_site_key },
Googlerecaptchaservice, AuthService, HidecomponentService, S3Service, AuthClientConfig],
bootstrap: [AppComponent]
yet now whenever I call my backend api I get this error:
ERROR Error: Login required
at new n (auth0-spa-js.production.esm.js:15)
at Function.n.fromPayload (auth0-spa-js.production.esm.js:15)
at s (auth0-spa-js.production.esm.js:15)
at ZoneDelegate.invokeTask (zone-evergreen.js:399)
at Object.onInvokeTask (core.js:27492)
at ZoneDelegate.invokeTask (zone-evergreen.js:398)
at Zone.runTask (zone-evergreen.js:167)
at ZoneTask.invokeTask [as invoke] (zone-evergreen.js:480)
at invokeTask (zone-evergreen.js:1621)
at globalZoneAwareCallback (zone-evergreen.js:1658)