Primus Auth
PrimusAuthService (Angular) and usePrimusAuth (React) are the primary interfaces for authentication in the Primus SDK. They handle token storage, session management, SSO redirects, and automatic token refresh.
Architecture
Your app → PrimusProvider / PrimusUiModule
↓
PrimusAuthService
↓
Primus Identity Broker API (/api/auth/*)
↓
Azure AD / Auth0 / Google / Okta
The SDK never talks to identity providers directly — all auth flows go through your Primus backend broker.
Setup
- React
- Angular
Wrap your app with PrimusProvider once at the root:
// main.tsx or App.tsx
import { PrimusProvider, PrimusThemeProvider, PrimusUiCoreBridge } from 'primus-react-ui';
export default function App() {
return (
<PrimusProvider
authority="https://api.yourdomain.com"
authBasePath="/api/auth"
clientId="my-app"
>
<PrimusThemeProvider defaultTheme="dark">
<PrimusUiCoreBridge />
<AppRoutes />
</PrimusThemeProvider>
</PrimusProvider>
);
}
Standalone (Angular 17+):
// main.ts
import { bootstrapApplication } from '@angular/platform-browser';
import { provideHttpClient } from '@angular/common/http';
import { providePrimus, providePrimusTheme } from 'primus-angular-ui';
import { AppComponent } from './app/app.component';
bootstrapApplication(AppComponent, {
providers: [
provideHttpClient(),
providePrimus({
apiBaseUrl: 'https://api.yourdomain.com',
authToken: () => localStorage.getItem('primus_token'),
}),
providePrimusTheme({ defaultTheme: 'dark' }),
],
});
NgModule (Angular 16–20):
import { PrimusUiModule } from 'primus-angular-ui';
@NgModule({
imports: [
PrimusUiModule.forRoot({
apiBaseUrl: 'https://api.yourdomain.com',
authToken: () => localStorage.getItem('primus_token'),
}),
],
})
export class AppModule {}
Sign in
- React
- Angular
import { usePrimusAuth } from 'primus-react-ui';
function LoginPage() {
const { login, loginWithProvider, isLoading, error } = usePrimusAuth();
// Email + password
const handleSubmit = async (email: string, password: string) => {
await login({ email, password });
// Redirects to /dashboard on success
};
// Social / SSO
const handleMicrosoft = () => loginWithProvider('azure');
const handleGoogle = () => loginWithProvider('google');
return (
<>
{error && <div role="alert" data-variant="danger">{error}</div>}
<button onClick={() => handleSubmit('jane@acme.com', 'secret')}>
Sign in
</button>
<button onClick={handleMicrosoft}>Continue with Microsoft</button>
</>
);
}
import { Component } from '@angular/core';
import { PrimusAuthService } from 'primus-angular-ui';
import { Router } from '@angular/router';
@Component({ templateUrl: './login.component.html' })
export class LoginComponent {
error = '';
isLoading = false;
constructor(
private auth: PrimusAuthService,
private router: Router,
) {}
async onSignIn(email: string, password: string) {
this.isLoading = true;
try {
await this.auth.signIn(email, password);
this.router.navigate(['/dashboard']);
} catch (err: any) {
this.error = err.message;
} finally {
this.isLoading = false;
}
}
signInWithMicrosoft() {
this.auth.signInWithProvider('azure');
}
signInWithGoogle() {
this.auth.signInWithProvider('google');
}
}
Check auth state
- React
- Angular
import { usePrimusAuth } from 'primus-react-ui';
function Dashboard() {
const { user, isAuthenticated, isLoading } = usePrimusAuth();
if (isLoading) return <div aria-busy="true" />;
if (!isAuthenticated) return <Navigate to="/login" />;
return <h1>Welcome, {user.name}</h1>;
}
import { PrimusAuthService } from 'primus-angular-ui';
import { Observable } from 'rxjs';
@Component({ template: `
<div *ngIf="auth.isLoading$ | async" aria-busy="true"></div>
<ng-container *ngIf="!(auth.isLoading$ | async)">
<h1 *ngIf="auth.isAuthenticated$ | async">
Welcome, {{ (auth.user$ | async)?.name }}
</h1>
</ng-container>
` })
export class DashboardComponent {
constructor(public auth: PrimusAuthService) {}
}
Sign out
- React
- Angular
const { logout } = usePrimusAuth();
<button onClick={() => logout()}>Sign out</button>
// Redirect after logout
<button onClick={() => logout({ returnTo: '/login' })}>Sign out</button>
// In your component
signOut() {
this.auth.signOut('/login'); // optional redirect path
}
<button (click)="signOut()">Sign out</button>
Route protection
- React
- Angular
import { usePrimusAuth } from 'primus-react-ui';
import { Navigate } from 'react-router-dom';
function RequireAuth({ children }: { children: React.ReactNode }) {
const { isAuthenticated, isLoading } = usePrimusAuth();
if (isLoading) return <div aria-busy="true" />;
if (!isAuthenticated) return <Navigate to="/login" replace />;
return <>{children}</>;
}
// Usage in router
<Route path="/dashboard" element={
<RequireAuth>
<Dashboard />
</RequireAuth>
} />
// auth.guard.ts
import { inject } from '@angular/core';
import { CanActivateFn, Router } from '@angular/router';
import { PrimusAuthService } from 'primus-angular-ui';
import { map, take } from 'rxjs/operators';
export const authGuard: CanActivateFn = () => {
const auth = inject(PrimusAuthService);
const router = inject(Router);
return auth.isAuthenticated$.pipe(
take(1),
map(isAuth => isAuth || router.createUrlTree(['/login'])),
);
};
// app.routes.ts
export const routes = [
{ path: 'dashboard', component: DashboardComponent, canActivate: [authGuard] },
{ path: 'login', component: LoginComponent },
];
Access the token
- React
- Angular
const { getToken } = usePrimusAuth();
// In an API call
const fetchData = async () => {
const token = await getToken();
const res = await fetch('/api/tenants', {
headers: { Authorization: `Bearer ${token}` },
});
return res.json();
};
The authToken resolver you provided to providePrimus / forRoot is called automatically on every HTTP request via the built-in interceptor. No manual header management needed.
// Token is injected automatically via PrimusAuthInterceptor
// Just call your API:
this.http.get('/api/tenants').subscribe(data => { ... });
To access the raw token manually:
const token = await this.auth.getToken();
API Reference
usePrimusAuth (React)
| Property / Method | Type | Description |
|---|---|---|
isAuthenticated | boolean | Whether the user is currently signed in |
isLoading | boolean | True while the auth state is being resolved |
user | PrimusUser | null | Current user object |
error | string | null | Last auth error message |
login(credentials) | Promise<void> | Sign in with email + password |
loginWithProvider(provider) | void | Start SSO redirect for a provider |
logout(options?) | Promise<void> | Sign out, optionally redirect |
getToken() | Promise<string> | Get the current access token |
PrimusAuthService (Angular)
| Property / Method | Type | Description |
|---|---|---|
isAuthenticated$ | Observable<boolean> | Auth state stream |
isLoading$ | Observable<boolean> | Loading state stream |
user$ | Observable<PrimusUser | null> | Current user stream |
signIn(email, password) | Promise<void> | Email/password sign in |
signInWithProvider(provider) | void | Start SSO redirect |
signOut(returnTo?) | void | Sign out |
getToken() | Promise<string> | Get access token |
PrimusUser
interface PrimusUser {
id: string;
email: string;
name: string;
roles: string[];
tenantId: string;
avatar?: string;
}
PrimusProvider props (React)
| Prop | Type | Default | Description |
|---|---|---|---|
authority | string | required | Your API base URL |
authBasePath | string | '/api/auth' | Auth endpoint prefix |
clientId | string | required | App client identifier |
providePrimus / forRoot options (Angular)
| Option | Type | Description |
|---|---|---|
apiBaseUrl | string | Your API base URL |
authToken | () => string | null | Token resolver called on every request |
Social providers
| Provider | loginWithProvider key | Notes |
|---|---|---|
| Microsoft / Azure AD | 'azure' | Requires Azure AD app registration |
'google' | Requires Google OAuth client | |
| Auth0 | 'auth0' | Requires Auth0 application |
| GitHub | 'github' | Requires GitHub OAuth app |
| Okta | 'okta' | Requires Okta application |
See the Identity Broker backend docs for provider configuration.