Jet provides comprehensive authentication using Supabase Auth, supporting password-based login, OAuth providers, and magic links (OTP).
Overview
Authentication is handled through the UserService which wraps Supabase’s authentication API. The service provides type-safe methods for all authentication operations and integrates seamlessly with Angular’s dependency injection system.
Supabase Client Setup
The Supabase client is configured as an injection token in src/app/injection-tokens/supabase-client.injection-token.ts:4:
export const SUPABASE_CLIENT : InjectionToken < SupabaseClient > = new InjectionToken < SupabaseClient >(
'SUPABASE_CLIENT' ,
{
factory : () =>
createClient (
import . meta . env . NG_APP_SUPABASE_URL ,
import . meta . env . NG_APP_SUPABASE_PUBLISHABLE_OR_ANON_KEY ,
{ auth: { throwOnError: true } },
),
providedIn: 'root' ,
},
);
The Supabase client is configured with throwOnError: true for better error handling throughout the application.
Authentication Methods
Password Authentication
Sign in with email and password:
protected async signInWithPassword ( email : string , password : string ) {
const { data } = await this . #userService . signInWithPassword ( email , password );
if ( data . session === null ) {
throw new Error ();
}
this . #alertService . showAlert ( this . #translocoService . translate ( 'alerts.welcome' ));
void this . #router . navigateByUrl ( this . returnUrl ());
}
OAuth Providers
Sign in with OAuth providers like Google:
protected async signInWithOauth ( oauthProvider : OauthProvider ) {
const { data } = await this . #userService . signInWithOauth ( oauthProvider );
window . location . href = data . url ?? '/' ;
}
Magic Link (OTP)
Send a passwordless sign-in link via email:
protected async signInWithOtp ( email : string ) {
await this . #userService . signInWithOtp ( email );
void this . #router . navigateByUrl ( '/sign-in-link-sent' );
}
User Service
The UserService (src/app/services/user/user.service.ts:23) provides all authentication operations:
@ Injectable ()
export class UserService {
readonly #supabaseClient = inject ( SUPABASE_CLIENT );
readonly #user : WritableSignal < null | User >;
public constructor () {
this . #user = signal ( null );
this . #supabaseClient . auth . onAuthStateChange (
( _authChangeEvent : AuthChangeEvent , authSession : AuthSession | null ) : void => {
this . #user . set ( authSession ?. user ?? null );
},
);
}
public get user () : Signal < null | User > {
return this . #user . asReadonly ();
}
}
Available Methods
getClaims() Retrieve JWT claims for the current session
signInWithPassword() Authenticate with email and password
signInWithOauth() Authenticate with OAuth providers
signInWithOtp() Send magic link for passwordless login
signUp() Create new user account
signOut() Sign out current user
updateUser() Update user attributes
resetPasswordForEmail() Send password reset email
Route Guards
Protect routes using the signedInGuard (src/app/guards/signed-in/signed-in.guard.ts:8):
export const signedInGuard : CanActivateFn = async (
_activatedRouteSnapshot ,
routerStateSnapshot ,
) : Promise < GuardResult > => {
const router = inject ( Router );
const userService = inject ( UserService );
let guardResult : GuardResult = router . createUrlTree ([ '/sign-in' ], {
queryParams: { [QueryParam.ReturnUrl]: routerStateSnapshot . url },
});
try {
const { data } = await userService . getClaims ();
if ( data !== null ) {
guardResult = true ;
}
} catch ( exception : unknown ) {
// Handle error
}
return guardResult ;
};
Using Guards in Routes
const routes : Routes = [
{
path: 'profile' ,
component: ProfilePageComponent ,
canActivate: [ signedInGuard ]
}
];
User Profiles
Jet includes a profiles table with Row Level Security (RLS) policies:
create table public .profiles (
user_id uuid primary key references auth . users (id),
avatar_url shared . url null ,
full_name text null check ( length (full_name) <= 60 ),
username text not null unique check (
length (username) between 3 and 36
and username ~ '^[a-z0-9_]+$'
),
created_at timestamptz not null default now (),
updated_at timestamptz null
);
RLS Policies
Users can only access and update their own profile:
create policy "Allow authenticated to select own" on public . profiles
for select to authenticated
using (( select auth . uid ()) = user_id);
create policy "Allow authenticated to update own" on public . profiles
for update to authenticated
using (( select auth . uid ()) = user_id)
with check (( select auth . uid ()) = user_id);
The sign-in page (src/app/components/sign-in-page/sign-in-page.component.ts:42) demonstrates a complete authentication flow:
protected readonly signInFormGroup : FormGroup <{
email : FormControl < null | string >;
password : FormControl < null | string >;
}>;
public constructor () {
this . signInFormGroup = this . #formBuilder . group ({
email: this . #formBuilder . control < null | string >( null , [
Validators . email ,
Validators . required ,
]),
password: this . #formBuilder . control < null | string >( null , [
Validators . minLength ( 6 ),
Validators . required ,
]),
});
}
Environment Variables
Authentication requires these environment variables to be configured:
NG_APP_SUPABASE_URL = your-project-url
NG_APP_SUPABASE_PUBLISHABLE_OR_ANON_KEY = your-anon-key
Next Steps
Supabase Integration Learn more about Supabase setup
Deployment Deploy with authentication