The UserService provides comprehensive authentication methods using Supabase Auth, including password-based auth, OAuth, magic links, and user management.
Import
import { UserService } from '@jet/services/user/user.service';
import { User } from '@supabase/supabase-js';
Usage
Access Current User
import { inject, Signal } from '@angular/core';
import { UserService } from '@jet/services/user/user.service';
import { User } from '@supabase/supabase-js';
export class ProfileComponent {
readonly #userService = inject(UserService);
readonly user: Signal<User | null> = this.#userService.user;
ngOnInit() {
const currentUser = this.user();
if (currentUser) {
console.log('User ID:', currentUser.id);
console.log('Email:', currentUser.email);
}
}
}
Sign Up with Email/Password
async signUp(email: string, password: string) {
try {
const { data, error } = await this.#userService.signUp(email, password);
if (error) {
console.error('Sign up failed:', error.message);
return;
}
console.log('User created:', data.user);
console.log('Check email for confirmation');
} catch (error) {
console.error('Sign up error:', error);
}
}
Sign In with Email/Password
async signIn(email: string, password: string) {
try {
const { data, error } = await this.#userService.signInWithPassword(email, password);
if (error) {
this.alertService.showErrorAlert('Invalid credentials');
return;
}
console.log('Signed in:', data.user);
this.router.navigate(['/dashboard']);
} catch (error) {
this.alertService.showErrorAlert('Sign in failed');
}
}
Sign In with OAuth
async signInWithGoogle() {
try {
const { data, error } = await this.#userService.signInWithOauth('google');
if (error) {
console.error('OAuth failed:', error.message);
return;
}
// Redirect to OAuth provider
if (data.url) {
window.location.href = data.url;
}
} catch (error) {
console.error('OAuth error:', error);
}
}
Sign In with Magic Link
async sendMagicLink(email: string) {
try {
const { error } = await this.#userService.signInWithOtp(email);
if (error) {
this.alertService.showErrorAlert('Failed to send magic link');
return;
}
this.alertService.showAlert('Check your email for the magic link');
} catch (error) {
console.error('Magic link error:', error);
}
}
Sign Out
async signOut() {
try {
const { error } = await this.#userService.signOut();
if (error) {
console.error('Sign out failed:', error.message);
return;
}
this.router.navigate(['/sign-in']);
} catch (error) {
console.error('Sign out error:', error);
}
}
Reset Password
async resetPassword(email: string) {
try {
const { error } = await this.#userService.resetPasswordForEmail(email);
if (error) {
this.alertService.showErrorAlert('Failed to send reset link');
return;
}
this.alertService.showAlert('Check your email for password reset link');
} catch (error) {
console.error('Reset password error:', error);
}
}
Update User
async updateEmail(newEmail: string) {
try {
const { data, error } = await this.#userService.updateUser({
email: newEmail
});
if (error) {
this.alertService.showErrorAlert('Failed to update email');
return;
}
this.alertService.showAlert('Email updated successfully');
} catch (error) {
console.error('Update user error:', error);
}
}
Properties
user
Read-only signal containing the current authenticated user.
Reactive signal that updates when auth state changes
Source: /home/daytona/workspace/source/src/app/services/user/user.service.ts:42
public get user(): Signal<null | User> {
return this.#user.asReadonly();
}
Example:
// Access in component
const currentUser = this.userService.user();
// Use in template
<div *ngIf="userService.user() as user">
<p>Welcome, {{ user.email }}</p>
</div>
// Use in computed signals
const isAuthenticated = computed(() => this.userService.user() !== null);
// Use in effects
effect(() => {
const user = this.userService.user();
if (user) {
console.log('User changed:', user.id);
}
});
Methods
signUp
Creates a new user account with email and password.
Promise resolving to auth response with user data and sessiondata
{ user: User | null; session: Session | null }
User and session data if successful
Error object if sign up failed
Source: /home/daytona/workspace/source/src/app/services/user/user.service.ts:84
public signUp(email: string, password: string): Promise<AuthResponse> {
return this.#supabaseClient.auth.signUp({
email,
options: { emailRedirectTo: this.#getRedirectUrlWithReturnUrl() },
password,
});
}
Example:
const { data, error } = await this.userService.signUp(
'[email protected]',
'securePassword123'
);
if (data.user) {
console.log('User created:', data.user.id);
console.log('Confirmation email sent');
}
signInWithPassword
Authenticates a user with email and password.
return
Promise<AuthTokenResponsePassword>
Promise resolving to auth response with user and session
Source: /home/daytona/workspace/source/src/app/services/user/user.service.ts:76
public signInWithPassword(email: string, password: string): Promise<AuthTokenResponsePassword> {
return this.#supabaseClient.auth.signInWithPassword({ email, password });
}
Example:
const { data, error } = await this.userService.signInWithPassword(
'[email protected]',
'password123'
);
if (data.session) {
console.log('Signed in:', data.user);
console.log('Access token:', data.session.access_token);
}
signInWithOauth
Initiates OAuth authentication flow with a provider.
OAuth provider to use (currently only ‘google’ is supported)
Promise resolving to OAuth response with provider URLdata
{ url: string | null; provider: string }
OAuth provider URL to redirect to
Error object if OAuth initialization failed
Source: /home/daytona/workspace/source/src/app/services/user/user.service.ts:62
public signInWithOauth(oauthProvider: OauthProvider): Promise<OAuthResponse> {
return this.#supabaseClient.auth.signInWithOAuth({
options: { redirectTo: this.#getRedirectUrlWithReturnUrl(), skipBrowserRedirect: true },
provider: oauthProvider,
});
}
Example:
const { data, error } = await this.userService.signInWithOauth('google');
if (data.url) {
// Redirect user to Google OAuth
window.location.href = data.url;
}
signInWithOtp
Sends a magic link (one-time password) to the user’s email.
User’s email address to send magic link to
Promise resolving to OTP responseError object if sending OTP failed
Source: /home/daytona/workspace/source/src/app/services/user/user.service.ts:69
public signInWithOtp(email: string): Promise<AuthOtpResponse> {
return this.#supabaseClient.auth.signInWithOtp({
email,
options: { emailRedirectTo: this.#getRedirectUrlWithReturnUrl(), shouldCreateUser: false },
});
}
Example:
const { error } = await this.userService.signInWithOtp('[email protected]');
if (!error) {
console.log('Magic link sent to email');
}
The shouldCreateUser: false option means magic links only work for existing users.
signOut
Signs out the current user and clears the session.
return
Promise<{ error: AuthError | null }>
Promise resolving to sign out result
Source: /home/daytona/workspace/source/src/app/services/user/user.service.ts:80
public signOut(): Promise<{ error: AuthError | null }> {
return this.#supabaseClient.auth.signOut();
}
Example:
const { error } = await this.userService.signOut();
if (!error) {
this.router.navigate(['/sign-in']);
this.storageService.clearLocalStorage();
}
resetPasswordForEmail
Sends a password reset email to the user.
Email address to send reset link to
return
Promise<PasswordResetResponse>
Promise resolving to password reset responseError object if sending reset email failed
Source: /home/daytona/workspace/source/src/app/services/user/user.service.ts:54
public resetPasswordForEmail(email: string): Promise<...> {
return this.#supabaseClient.auth.resetPasswordForEmail(email, {
redirectTo: this.#getRedirectUrlWithReturnUrl('/update-password'),
});
}
Example:
const { error } = await this.userService.resetPasswordForEmail('[email protected]');
if (!error) {
console.log('Password reset email sent');
}
updateUser
Updates the current user’s attributes.
Object containing attributes to update
Promise resolving to user response with updated user data
Source: /home/daytona/workspace/source/src/app/services/user/user.service.ts:92
public updateUser(userAttributes: UserAttributes): Promise<UserResponse> {
return this.#supabaseClient.auth.updateUser(userAttributes);
}
Example:
// Update email
const { data } = await this.userService.updateUser({
email: '[email protected]'
});
// Update password
const { data } = await this.userService.updateUser({
password: 'newSecurePassword123'
});
// Update custom metadata
const { data } = await this.userService.updateUser({
data: { preferences: { theme: 'dark' } }
});
getClaims
Retrieves the current user’s JWT claims.
Promise resolving to JWT claims datadata
{ claims: JwtPayload; header: JwtHeader; signature: Uint8Array } | null
Decoded JWT token data
Error object if retrieval failed
Source: /home/daytona/workspace/source/src/app/services/user/user.service.ts:46
public getClaims(): Promise<...> {
return this.#supabaseClient.auth.getClaims();
}
Example:
const { data, error } = await this.userService.getClaims();
if (data) {
console.log('User ID:', data.claims.sub);
console.log('Email:', data.claims.email);
console.log('Token expires:', data.claims.exp);
}
Type Definitions
OauthProvider Type
import { Provider } from '@supabase/supabase-js';
export type OauthProvider = Extract<Provider, 'google'>;
Source: /home/daytona/workspace/source/src/app/types/oauth-provider.type.ts:3
Features
Reactive User State
The user signal automatically updates when auth state changes:
this.#supabaseClient.auth.onAuthStateChange(
(_authChangeEvent: AuthChangeEvent, authSession: AuthSession | null): void => {
this.#user.set(authSession?.user ?? null);
},
);
Automatic Redirects
All auth methods include redirect URLs with optional return paths:
// Reads from query params
const returnUrl = this.#activatedRoute.snapshot.queryParamMap.get(QueryParam.ReturnUrl);
// Builds redirect URL
const redirectUrl = new URL('/sign-in', window.location.origin);
if (returnUrl) {
redirectUrl.searchParams.set(QueryParam.ReturnUrl, returnUrl);
}
Email Confirmation
Sign up automatically sends confirmation emails:
this.#supabaseClient.auth.signUp({
email,
options: { emailRedirectTo: this.#getRedirectUrlWithReturnUrl() },
password,
});
Configuration
Supabase Setup
The service requires Supabase client injection:
import { SUPABASE_CLIENT } from '@jet/injection-tokens/supabase-client.injection-token';
import { createClient } from '@supabase/supabase-js';
import { environment } from './environments/environment';
const supabaseClient = createClient(
environment.supabaseUrl,
environment.supabaseAnonKey
);
export const appConfig: ApplicationConfig = {
providers: [
{ provide: SUPABASE_CLIENT, useValue: supabaseClient },
UserService // Must be explicitly provided
]
};
Best Practices
- Handle errors properly - Always check for errors in auth responses
- Use reactive state - Access user through the signal for automatic updates
- Redirect after auth - Navigate users after successful authentication
- Clear storage on sign out - Remove sensitive data when users log out
- Validate inputs - Check email format and password strength before calling auth methods
- Show loading states - Display spinners during async auth operations
Common Patterns
Auth Guard
export const authGuard: CanActivateFn = (route, state) => {
const userService = inject(UserService);
const router = inject(Router);
const user = userService.user();
if (!user) {
router.navigate(['/sign-in'], {
queryParams: { returnUrl: state.url }
});
return false;
}
return true;
};
Sign In Component
export class SignInComponent {
readonly #userService = inject(UserService);
readonly #router = inject(Router);
readonly #alertService = inject(AlertService);
signInForm = new FormGroup({
email: new FormControl('', [Validators.required, Validators.email]),
password: new FormControl('', Validators.required)
});
async onSubmit() {
if (!this.signInForm.valid) return;
const { email, password } = this.signInForm.value;
const { error } = await this.#userService.signInWithPassword(email!, password!);
if (error) {
this.#alertService.showErrorAlert('Invalid email or password');
return;
}
this.#router.navigate(['/dashboard']);
}
async signInWithGoogle() {
const { data, error } = await this.#userService.signInWithOauth('google');
if (error || !data.url) {
this.#alertService.showErrorAlert('Failed to sign in with Google');
return;
}
window.location.href = data.url;
}
}
Dependencies
The UserService depends on:
- SUPABASE_CLIENT - Supabase client instance
- LoggerService - For service initialization logging
- ActivatedRoute - For reading query parameters
This service is not provided at the root level. You must provide it in your component or module providers.