Yet another 3rd-party cookie issue

I have read through probably 10 different posts about this and have been trying to figure this out for most of my day. I keep getting the “Login required” error, no matter what I do in Mac Safari and Google Chrome while in Incognito mode.

Here is where I am at. Firstly, I added these properties:

image

I updated auth0/angular to v2.2.3.

I went in to my tenant, and for my application, i set this:

I went to my API and set this:

My interceptor, which has been working great for months, looks like this:

import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { AuthService } from '@auth0/auth0-angular';
import { mergeMap, Observable } from 'rxjs';
import { environment } from '../environments/environment';

@Injectable()
export class AuthHttpInterceptor implements HttpInterceptor {
	public constructor(private readonly auth: AuthService) {}

	public intercept(
		request: HttpRequest<unknown>,
		next: HttpHandler
	): Observable<HttpEvent<unknown>> {
		if (request.url.toLowerCase().startsWith(environment.apiRoot)) {
			return this.auth.getAccessTokenSilently().pipe(
				mergeMap((token) => {
					const tokenReq: HttpRequest<unknown> = request.clone({
						setHeaders: { Authorization: `Bearer ${token}` },
					});
					return next.handle(tokenReq);
				})
			);
		} else {
			return next.handle(request);
		}
	}
}

the getAccessTokenSilently call throws the error.

I have tried all kinds of combinations of the above settings, tried adding in a check for the ‘login_required’ error and doing a redirect (which sends it spiraling into an infinite loop of redirects). ChatGPT just regurgitates what’s already on this board, so that’s not helpful either. I am out of options. I have no idea what else to try.

Thanks for any information.

I see on your example on a github page that you guys are just ignoring this error:

try {
  await auth0.getTokenSilently();
} catch (error) {
  if (error.error !== 'login_required') {
    throw error;
  }
}

Modifying my code:

	public intercept(
		request: HttpRequest<unknown>,
		next: HttpHandler
	): Observable<HttpEvent<unknown>> {
		if (request.url.toLowerCase().startsWith(environment.apiRoot)) {
			return this.auth.getAccessTokenSilently().pipe(
				mergeMap((token) => {
					const tokenReq: HttpRequest<unknown> = request.clone({
						setHeaders: { Authorization: `Bearer ${token}` },
					});
					return next.handle(tokenReq);
				}),
				catchError((error: HttpErrorResponse) => {
					if (error.error === 'login_required') {
						return next.handle(request);
					}
					return throwError(() => error);
				})
			);
		} else {
			return next.handle(request);
		}
	}

Is that all I’m missing?

The reason this happens in Chrome Incognito and sometimes in Safari is that they block what they perceive to be third-party cookies. When you do a getAccessTokenSilently() by default it sends a request to Auth0 in a hidden iframe (to avoid page refresh) which faces this problem and Auth0 thinks there’s no existing session - hence the login_required error.

Also keep in mind that if the user hasn’t logged in at all before, the login_required error is expected and normal. This may not be your case, but worth mentioning.

There are two solutions generally:

  1. Use a Custom Domain that shares the parent domain with the app’s domain. This causes the browsers to treat the cookies as first-party.
  2. Use refresh tokens

When you use refresh tokens, Auth0 will send a refresh token after the initial authentication and the app exchanges it later when getAccessTokenSilently is called. This works because the refresh token flow does not rely on cookies. If this fails for some reason the SDK will fall back the normal cookie-based silent auth method.

I see that the code snippets you posted include refresh token configuration, so ideally this should work. But given you are still seeing errors, some questions we can ask at this stage are:

  1. Is the user actually logging in before the app calls getAccessTokenSilently? If not, you can just ignore the login_required error as you have done now.
  2. Is the app actually getting back refresh tokens? You’ll be able to check this by inspecting the POST /oauth/token request in the Network tab in browser’s dev tools.

Also make sure that the session timeouts in tenant settings → Advanced tab aren’t set to low values. If they are low (like a few minutes) the session may have actually expired by the time getAccessTokenSilently is called.

the token endpoint returns:

I added that code that ignores that error… and now it’s not working at all – meaning, it’s not injecting the token to the outgoing requests, meaning that every time I’m calling getAccessTokenSilently, it’s throwing that specific error, which I am ignoring.

I can get it to start working by refreshing the page.

It uses the token immediately after signing in. There is no wait period after logging in and actually using the token as it talks to a backend API immediately to get data to display.

Interesting. Theoretically the app should keep using the refresh token if it receives one and should not throw any login_required errors.

You can try downloading the Angular quickstart, modify the config to use refresh tokens and test that to see if you get the problem there as well. If you don’t, that might point to some config issue in your actual app.

I’m using [AuthGuard] on my routes so if someone tries to go to the route, it automatically redirects to the login page. Then it redirects back and that’s when the error gets thrown.

The application you posted isn’t the same flow. I don’t have time to implement some other flow which will probably work fine. I need it to work with routes protected with [AuthGuard]. Is it something specific with this type of flow?

I don’t know if this will help, but here is what’s happening. You can see the call to token and then the call to auth0’s “authorize” endpoint here:

This is what the authorize request looks like:

The 3 “User” calls are going to my backend without a bearer token due to the code above ignoring the “login_required” error. My backend is rejecting those calls because no token is in the auth header.

So what is missing. I have all the pieces in place. The refresh token is there, why is Auth0 still throwing the “login_required” error when the token call is returning valid data.

The quickstart I pasted in fact has some routes that use [AuthGuard] (you can download the repo from the Github link there). The quickstart uses some of Auth0-recommended practices. I just tested it after adding useRefreshTokens and cacheLocation settings in a Chrome Incognito window and it seems to work fine so far. So it’ll help to check if there’s any diff between the implementations.

I don’t have time to implement some other flow which will probably work fine.

The idea is to start with a known working implementation and try to break it, if it’s hard to figure out what is not working in your current app. If nothing works, I would recommend opening a support ticket with a minimal reproducible sample and a HAR file of the flow so the team will be able to take a closer look.