
Auth0 Ionic Angular Skill
- 294 installs
- 39 repo stars
- auth0/agent-skills
Add Auth0 authentication to Ionic Angular mobile applications with full SDK integration.
About
Auth0 Ionic Angular integration enables secure authentication in Ionic Angular apps. Mobile developers use it to add user login without building custom auth systems.
- Ionic Angular
- Auth0 SDK
- Mobile auth
Auth0 Ionic Angular by the numbers
- 294 all-time installs (skills.sh)
- Ranked #375 of 1,049 Mobile Development skills by installs in the Skillselion catalog
- Data as of Aug 11, 2026 (Skillselion catalog sync)
npx skills add https://github.com/auth0/agent-skills --skill auth0-ionic-angularAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 294 |
|---|---|
| repo stars | ★ 39 |
| Repository | auth0/agent-skills ↗ |
What it does
Add Auth0 authentication to Ionic Angular mobile applications with full SDK integration.
Files
Auth0 Ionic Angular (Capacitor) Integration
Add authentication to an Ionic Angular application using the @auth0/auth0-angular SDK with Capacitor plugins for native iOS and Android. This skill covers login, logout, user profile display, and secure token management using the system browser (SFSafariViewController on iOS, Chrome Custom Tabs on Android) via Capacitor's Browser plugin.
Prerequisites
- Node.js 20+ and npm 10+
- Ionic CLI (
npm install -g @ionic/cli) - Capacitor 5+ configured in the project
- Auth0 CLI (for automatic setup):
brew install auth0/auth0-cli/auth0 - An Auth0 account (free tier works)
When NOT to Use
| Use Case | Recommended Skill |
|---|---|
| Ionic React app with Capacitor | auth0-ionic-react |
| Ionic Vue app with Capacitor | auth0-ionic-vue |
| Angular SPA (browser-only, no Capacitor) | auth0-angular or auth0-react |
| React Native (no Ionic) | auth0-react-native |
| Expo (React Native) | auth0-expo |
| Native iOS (Swift) | auth0-swift |
| Native Android (Kotlin) | auth0-android |
Quick Start Workflow
Agent instruction: Follow these steps in order. Always use AskUserQuestion to let the developer choose between Automatic Setup and Manual Setup before proceeding — even if credentials are already provided in the prompt.>
SECURITY — Never display credentials: After obtaining Auth0 credentials (domain, client ID) via the CLI or from a file, NEVER print, echo, or display them in your text output. Write them directly to the config file (src/environments/environment.ts) silently. Do NOT produce output like "Domain: xxx" or "Client ID: yyy". Instead, confirm that the config file has been written and tell the user where to find it.>
UI reuse: Before creating new login/logout components, search the existing project for login/logout handlers or buttons. If found, hook Auth0 into the existing UI rather than creating duplicate components.
Step 1: Install Dependencies
npm install @auth0/auth0-angular @capacitor/browser @capacitor/appStep 2: Configure Auth0
Agent instruction: Always present the setup choice using AskUserQuestion — even if the user has already provided credentials:>
```
AskUserQuestion:
question: "How would you like to configure Auth0 for your Ionic Angular app?"
options:
- label: "Automatic Setup (Recommended)"
description: "Uses the Auth0 CLI to create a Native application, configure callback URLs, and store credentials in your project automatically."
- label: "Manual Setup"
description: "You provide an .env file with your Auth0 Domain and Client ID, and the agent reads it and writes the project configuration for you."
```
>
Follow the chosen path in the Setup Guide which has the full step-by-step instructions for both options.
Auth0 Dashboard settings (Native application type):
| Setting | Value |
|---|---|
| Application Type | Native |
| Allowed Callback URLs | PACKAGE_ID://YOUR_DOMAIN/capacitor/PACKAGE_ID/callback |
| Allowed Logout URLs | PACKAGE_ID://YOUR_DOMAIN/capacitor/PACKAGE_ID/callback |
| Allowed Origins | capacitor://localhost, http://localhost |
Replace PACKAGE_ID with your appId from capacitor.config.ts (e.g., com.example.myapp) and YOUR_DOMAIN with your Auth0 domain.
Note: For Automatic Setup, these URLs are configured automatically by the Auth0 CLI. For Manual Setup, the user must configure them in the Auth0 Dashboard.
Note: For local web development (ionic serve), also addhttp://localhost:8100to Allowed Callback URLs, Allowed Logout URLs, and Allowed Web Origins.
Step 3: Configure the SDK
In src/app/app.module.ts (NgModule) or src/app/app.config.ts (standalone):
The provideAuth0() function (or AuthModule.forRoot()) is the Angular equivalent of Auth0Provider — it acts as the provider/wrapper that wraps the app and makes AuthService available everywhere. For local web development with ionic serve, the callback URL is http://localhost:8100.
Standalone (Angular 17+):
import { ApplicationConfig } from '@angular/core';
import { provideAuth0 } from '@auth0/auth0-angular';
// Replace with your capacitor.config.ts appId and Auth0 domain
const appId = 'com.example.myapp';
const domain = 'YOUR_AUTH0_DOMAIN';
const callbackUri = `${appId}://${domain}/capacitor/${appId}/callback`;
export const appConfig: ApplicationConfig = {
providers: [
provideAuth0({
domain,
clientId: 'YOUR_AUTH0_CLIENT_ID',
useRefreshTokens: true,
useRefreshTokensFallback: false,
authorizationParams: {
redirect_uri: callbackUri,
},
}),
],
};NgModule (Angular 16 and earlier):
import { AuthModule } from '@auth0/auth0-angular';
const appId = 'com.example.myapp';
const domain = 'YOUR_AUTH0_DOMAIN';
const callbackUri = `${appId}://${domain}/capacitor/${appId}/callback`;
@NgModule({
imports: [
AuthModule.forRoot({
domain,
clientId: 'YOUR_AUTH0_CLIENT_ID',
useRefreshTokens: true,
useRefreshTokensFallback: false,
authorizationParams: {
redirect_uri: callbackUri,
},
}),
],
})
export class AppModule {}Step 4: Handle Deep Link Callbacks (AppComponent)
Register the appUrlOpen listener at the app root so it persists across navigation:
import { Component, NgZone, OnInit } from '@angular/core';
import { AuthService } from '@auth0/auth0-angular';
import { Browser } from '@capacitor/browser';
import { App as CapApp } from '@capacitor/app';
import { mergeMap } from 'rxjs/operators';
@Component({
selector: 'app-root',
template: `<ion-app><ion-router-outlet></ion-router-outlet></ion-app>`,
})
export class AppComponent implements OnInit {
constructor(
private auth: AuthService,
private ngZone: NgZone
) {}
ngOnInit() {
CapApp.addListener('appUrlOpen', ({ url }) => {
this.ngZone.run(() => {
if (url.includes('state') && (url.includes('code') || url.includes('error'))) {
this.auth
.handleRedirectCallback(url)
.pipe(mergeMap(() => Browser.close()))
.subscribe();
}
});
});
}
}Step 5: Implement Login
import { Component } from '@angular/core';
import { AuthService } from '@auth0/auth0-angular';
import { Browser } from '@capacitor/browser';
@Component({
selector: 'app-login',
template: `<ion-button (click)="login()">Log In</ion-button>`,
})
export class LoginPage {
constructor(public auth: AuthService) {}
login() {
this.auth
.loginWithRedirect({
async openUrl(url: string) {
await Browser.open({ url, windowName: '_self' });
},
})
.subscribe();
}
}Step 6: Implement Logout
import { Component } from '@angular/core';
import { AuthService } from '@auth0/auth0-angular';
import { Browser } from '@capacitor/browser';
@Component({
selector: 'app-logout-button',
template: `<ion-button (click)="logout()">Log Out</ion-button>`,
})
export class LogoutButtonComponent {
constructor(public auth: AuthService) {}
logout() {
this.auth
.logout({
logoutParams: {
returnTo: `YOUR_PACKAGE_ID://YOUR_AUTH0_DOMAIN/capacitor/YOUR_PACKAGE_ID/callback`,
},
async openUrl(url: string) {
await Browser.open({ url, windowName: '_self' });
},
})
.subscribe();
}
}Step 7: Display User Profile
import { Component } from '@angular/core';
import { AuthService } from '@auth0/auth0-angular';
import { AsyncPipe } from '@angular/common';
@Component({
selector: 'app-profile',
template: `
<div *ngIf="auth.user$ | async as user">
<img [src]="user.picture" [alt]="user.name" />
<h2>{{ user.name }}</h2>
<p>{{ user.email }}</p>
</div>
`,
})
export class ProfileComponent {
constructor(public auth: AuthService) {}
}Step 8: Build and Test
Agent instruction: After writing all code, verify the build succeeds:
```bash
npm run build
npx cap sync
```
If the build fails, investigate errors and fix (up to 5-6 iterations). If still failing, use AskUserQuestion to ask the user for help.Detailed Documentation
- [Setup Guide](./references/setup.md) — Auth0 configuration, Auth0 CLI setup, Capacitor platform setup, deep linking
- [Integration Patterns](./references/integration.md) — Login/logout flows, token management, user profile, error handling, Capacitor lifecycle
- [API Reference & Testing](./references/api.md) — AuthService API, configuration options, claims reference, testing checklist
Common Mistakes
| Mistake | Fix |
|---|---|
| Auth0 app type set to SPA instead of Native | Change to Native in Auth0 Dashboard → Application Settings |
| Missing callback URL in Auth0 Dashboard | Add PACKAGE_ID://{domain}/capacitor/PACKAGE_ID/callback to Allowed Callback URLs AND Allowed Logout URLs |
Not wrapping handleRedirectCallback in ngZone.run() | Angular won't detect auth state changes — always wrap in ngZone.run() |
Using window.location.href for login redirect | Must use Browser.open() from @capacitor/browser for system browser |
useRefreshTokens not set to true | Required for mobile — localStorage is unreliable on native platforms |
useRefreshTokensFallback not set to false | Must be false to avoid falling back to iframe-based token refresh (unsupported on mobile) |
Missing @capacitor/app listener for deep links | The appUrlOpen listener is required to handle the callback from the system browser |
Using loginWithPopup on mobile | Popups don't work on native — use loginWithRedirect with Browser.open |
| Callback URL mismatch (scheme vs package ID) | The URL scheme must match the appId in capacitor.config.ts exactly |
WebAuth Method
Ionic with Capacitor uses the Web Auth method for authentication:
1. User taps Log In → app calls loginWithRedirect with a custom openUrl that uses Browser.open() 2. Capacitor's Browser plugin opens the Auth0 Universal Login page in the system browser (SFSafariViewController / Chrome Custom Tabs) 3. User authenticates → Auth0 redirects to the custom URL scheme callback 4. OS routes the deep link to your app → appUrlOpen event fires 5. handleRedirectCallback(url) processes the auth code exchange inside ngZone.run() 6. Browser.close() dismisses the system browser 7. auth.isAuthenticated$ emits true, and auth.user$ emits the user profile
Related Skills
- auth0-ionic-react — Ionic React with Capacitor
- auth0-ionic-vue — Ionic Vue with Capacitor
- auth0-angular — Angular SPA (browser-only)
- auth0-swift — Native iOS (Swift)
- auth0-android — Native Android (Kotlin)
Quick Reference
| API | Description |
|---|---|
AuthService.loginWithRedirect(options) | Start login flow with custom openUrl for Capacitor |
AuthService.logout(options) | Log out with custom openUrl and returnTo |
AuthService.handleRedirectCallback(url) | Process the callback URL from the deep link |
AuthService.isAuthenticated$ | Observable boolean — whether user is logged in |
AuthService.user$ | Observable — current user profile (name, email, picture) |
AuthService.isLoading$ | Observable boolean — SDK initialization state |
AuthService.error$ | Observable — authentication errors |
AuthService.getAccessTokenSilently() | Get access token (uses refresh tokens on mobile) |
Browser.open({ url }) | Open URL in system browser (Capacitor) |
CapApp.addListener('appUrlOpen', cb) | Listen for deep link callbacks (Capacitor) |
References
auth0-ionic-angular — API Reference & Testing
Configuration Options
provideAuth0() / AuthModule.forRoot() Options
| Option | Type | Required | Default | Description |
|---|---|---|---|---|
domain | string | Yes | — | Auth0 tenant domain (e.g., your-tenant.auth0.com) |
clientId | string | Yes | — | Auth0 application Client ID |
useRefreshTokens | boolean | Yes (mobile) | false | Must be true for Ionic Capacitor apps |
useRefreshTokensFallback | boolean | Yes (mobile) | true | Must be false for Ionic Capacitor apps |
cacheLocation | `'memory' \ | 'localstorage'` | No | 'memory' |
authorizationParams.redirect_uri | string | Yes (Capacitor) | window.location.origin | Must be set to custom URL scheme for Capacitor: PACKAGE_ID://DOMAIN/capacitor/PACKAGE_ID/callback |
authorizationParams.audience | string | No | — | API audience for access token scoping |
authorizationParams.scope | string | No | 'openid profile email' | OAuth scopes to request |
httpInterceptor.allowedList | `string[] \ | HttpInterceptorRouteConfig[]` | No | [] |
errorPath | string | No | — | Route to redirect to on authentication error |
Capacitor Configuration (capacitor.config.ts)
import type { CapacitorConfig } from '@capacitor/cli';
const config: CapacitorConfig = {
appId: 'com.example.myapp', // Used as URL scheme
appName: 'My Ionic App',
webDir: 'www',
server: {
androidScheme: 'https',
},
};
export default config;AuthService API
Properties (Observables)
| Property | Type | Description |
|---|---|---|
isAuthenticated$ | Observable<boolean> | Emits true when user is authenticated |
isLoading$ | Observable<boolean> | Emits true while SDK is initializing |
user$ | `Observable<User \ | null \ |
error$ | `Observable<Error \ | undefined>` |
idTokenClaims$ | `Observable<IdToken \ | null \ |
Methods
| Method | Returns | Description |
|---|---|---|
loginWithRedirect(options?) | Observable<void> | Redirect to Auth0 Universal Login. Pass openUrl for Capacitor. |
logout(options?) | Observable<void> | Log out and redirect. Pass openUrl and logoutParams.returnTo for Capacitor. |
handleRedirectCallback(url?) | Observable<RedirectLoginResult> | Process callback URL from deep link. Call inside ngZone.run(). |
getAccessTokenSilently(options?) | Observable<string> | Get access token using refresh token (no iframe on mobile). |
getAccessTokenWithPopup(options?) | Observable<string> | Not supported on mobile — use getAccessTokenSilently(). |
loginWithRedirect Options (Capacitor)
// callbackUri = `${appId}://${domain}/capacitor/${appId}/callback`
this.auth.loginWithRedirect({
authorizationParams: {
audience: 'https://my-api.example.com',
scope: 'openid profile email read:data',
redirect_uri: callbackUri,
},
async openUrl(url: string) {
await Browser.open({ url, windowName: '_self' });
},
}).subscribe();logout Options (Capacitor)
// callbackUri = `${appId}://${domain}/capacitor/${appId}/callback`
this.auth.logout({
logoutParams: {
returnTo: callbackUri,
},
async openUrl(url: string) {
await Browser.open({ url, windowName: '_self' });
},
}).subscribe();Claims Reference
Standard OIDC Claims (user$)
| Claim | Type | Description |
|---|---|---|
sub | string | Unique user identifier (e.g., `auth0\ |
name | string | Full name |
given_name | string | First name |
family_name | string | Last name |
nickname | string | Casual name |
picture | string | Profile picture URL |
email | string | Email address |
email_verified | boolean | Whether email is verified |
locale | string | User locale |
updated_at | string | Last profile update timestamp |
Auth0-Specific Claims
| Claim | Type | Source | Description |
|---|---|---|---|
org_id | string | Organizations | Organization identifier |
permissions | string[] | RBAC | Granted permissions (requires API audience + RBAC enabled) |
HTTP Interceptor for API Calls
Attach access tokens to outgoing API requests automatically:
provideAuth0({
domain: 'YOUR_AUTH0_DOMAIN',
clientId: 'YOUR_AUTH0_CLIENT_ID',
useRefreshTokens: true,
useRefreshTokensFallback: false,
authorizationParams: {
audience: 'https://my-api.example.com',
},
httpInterceptor: {
allowedList: [
'https://my-api.example.com/*',
{
uri: 'https://my-api.example.com/admin/*',
tokenOptions: {
authorizationParams: {
scope: 'admin:access',
},
},
},
],
},
}),
provideHttpClient(withInterceptors([authHttpInterceptorFn])),Complete Minimal Example
src/app/app.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { provideAuth0, authHttpInterceptorFn } from '@auth0/auth0-angular';
import { routes } from './app.routes';
const appId = 'YOUR_PACKAGE_ID';
const domain = 'YOUR_AUTH0_DOMAIN';
const callbackUri = `${appId}://${domain}/capacitor/${appId}/callback`;
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes),
provideHttpClient(withInterceptors([authHttpInterceptorFn])),
provideAuth0({
domain,
clientId: 'YOUR_AUTH0_CLIENT_ID',
useRefreshTokens: true,
useRefreshTokensFallback: false,
authorizationParams: {
redirect_uri: callbackUri,
},
}),
],
};src/app/app.component.ts
import { Component, NgZone, OnInit } from '@angular/core';
import { AuthService } from '@auth0/auth0-angular';
import { Browser } from '@capacitor/browser';
import { App as CapApp } from '@capacitor/app';
import { IonApp, IonRouterOutlet } from '@ionic/angular/standalone';
import { mergeMap } from 'rxjs/operators';
@Component({
selector: 'app-root',
standalone: true,
imports: [IonApp, IonRouterOutlet],
template: `
<ion-app>
<ion-router-outlet></ion-router-outlet>
</ion-app>
`,
})
export class AppComponent implements OnInit {
constructor(
private auth: AuthService,
private ngZone: NgZone
) {}
ngOnInit() {
CapApp.addListener('appUrlOpen', ({ url }) => {
this.ngZone.run(() => {
if (url.includes('state') && (url.includes('code') || url.includes('error'))) {
this.auth
.handleRedirectCallback(url)
.pipe(mergeMap(() => Browser.close()))
.subscribe();
}
});
});
}
}Testing Checklist
- [ ] App opens Auth0 Universal Login in system browser (not in-app WebView)
- [ ] After login, system browser closes and app receives user profile
- [ ]
auth.isAuthenticated$emitstrueafter successful login - [ ]
auth.user$contains name, email, and picture - [ ] Logout opens system browser, clears session, and returns to app
- [ ] After logout,
auth.isAuthenticated$emitsfalse - [ ] Token refresh works silently (no login prompt on app restart if session valid)
- [ ] Deep link callback URL matches Auth0 Dashboard configuration exactly
- [ ] App works on both iOS (SFSafariViewController) and Android (Chrome Custom Tabs)
- [ ]
ngZone.run()wraps all callback handling (UI updates correctly) - [ ] Build succeeds:
npm run build && npx cap sync
Common Issues
| Symptom | Cause | Fix |
|---|---|---|
| Login opens but never returns to app | Callback URL mismatch | Ensure PACKAGE_ID://{domain}/capacitor/PACKAGE_ID/callback is in Auth0 Dashboard |
| UI doesn't update after login | Missing ngZone.run() | Wrap handleRedirectCallback in this.ngZone.run() |
getAccessTokenSilently fails | useRefreshTokens not true | Set useRefreshTokens: true and useRefreshTokensFallback: false |
| "Callback URL mismatch" error | Wrong app type in Auth0 | Change application type to Native (not SPA) |
| White screen after login on Android | androidScheme not set | Add server: { androidScheme: 'https' } to capacitor.config.ts |
| Token lost on app restart | Cache location issue | Ensure useRefreshTokens: true for persistent sessions |
Browser.open not available | Missing Capacitor plugin | Run npm install @capacitor/browser && npx cap sync |
Security Considerations
- Never store tokens in localStorage on mobile — use
useRefreshTokens: truewith in-memory cache - Never embed Client Secret in mobile apps — Native apps use PKCE (no secret needed)
- Always validate the callback URL contains
stateandcode/errorbefore callinghandleRedirectCallback - Use HTTPS for any API calls made with access tokens
- Set `useRefreshTokensFallback: false` to prevent iframe-based token refresh attempts on mobile
auth0-ionic-angular — Integration Patterns
Authentication Flow Overview
User taps Login
→ auth.loginWithRedirect({ openUrl: Browser.open })
→ System browser opens Auth0 Universal Login
→ User authenticates
→ Auth0 redirects to custom URL scheme
→ OS routes deep link to app
→ CapApp.addListener('appUrlOpen') fires
→ ngZone.run() → auth.handleRedirectCallback(url)
→ Browser.close()
→ auth.isAuthenticated$ emits true
→ auth.user$ emits user profileDeep Link Callback Handler
The callback handler must be registered early in the app lifecycle. The recommended location is AppComponent.ngOnInit():
import { Component, NgZone, OnInit } from '@angular/core';
import { AuthService } from '@auth0/auth0-angular';
import { Browser } from '@capacitor/browser';
import { App as CapApp } from '@capacitor/app';
import { mergeMap } from 'rxjs/operators';
@Component({
selector: 'app-root',
standalone: true,
imports: [IonApp, IonRouterOutlet],
template: `
<ion-app>
<ion-router-outlet></ion-router-outlet>
</ion-app>
`,
})
export class AppComponent implements OnInit {
constructor(
private auth: AuthService,
private ngZone: NgZone
) {}
ngOnInit() {
CapApp.addListener('appUrlOpen', ({ url }) => {
this.ngZone.run(() => {
if (url.includes('state') && (url.includes('code') || url.includes('error'))) {
this.auth
.handleRedirectCallback(url)
.pipe(mergeMap(() => Browser.close()))
.subscribe();
}
});
});
}
}Why `ngZone.run()`? Capacitor plugin callbacks execute outside Angular's zone. Without ngZone.run(), Angular won't detect the authentication state change and the UI won't update.
Login
Basic Login
import { Component } from '@angular/core';
import { AuthService } from '@auth0/auth0-angular';
import { Browser } from '@capacitor/browser';
@Component({
selector: 'app-login',
template: `
<ion-button (click)="login()" *ngIf="(auth.isAuthenticated$ | async) === false">
Log In
</ion-button>
`,
})
export class LoginPage {
constructor(public auth: AuthService) {}
login() {
this.auth
.loginWithRedirect({
async openUrl(url: string) {
await Browser.open({ url, windowName: '_self' });
},
})
.subscribe();
}
}Login with Custom Audience and Scopes
login() {
this.auth
.loginWithRedirect({
authorizationParams: {
audience: 'https://my-api.example.com',
scope: 'openid profile email read:data',
},
async openUrl(url: string) {
await Browser.open({ url, windowName: '_self' });
},
})
.subscribe();
}Login with Organization
login() {
this.auth
.loginWithRedirect({
authorizationParams: {
organization: 'org_abc123',
},
async openUrl(url: string) {
await Browser.open({ url, windowName: '_self' });
},
})
.subscribe();
}Logout
Basic Logout
import { Component } from '@angular/core';
import { AuthService } from '@auth0/auth0-angular';
import { Browser } from '@capacitor/browser';
@Component({
selector: 'app-logout-button',
template: `
<ion-button (click)="logout()" *ngIf="auth.isAuthenticated$ | async">
Log Out
</ion-button>
`,
})
export class LogoutButtonComponent {
constructor(public auth: AuthService) {}
logout() {
this.auth
.logout({
logoutParams: {
returnTo: `PACKAGE_ID://YOUR_AUTH0_DOMAIN/capacitor/PACKAGE_ID/callback`,
},
async openUrl(url: string) {
await Browser.open({ url, windowName: '_self' });
},
})
.subscribe();
}
}Building the Logout Return URL Dynamically
import { Inject } from '@angular/core';
import { AuthClientConfig } from '@auth0/auth0-angular';
import { DOCUMENT } from '@angular/common';
export class LogoutButtonComponent {
constructor(
public auth: AuthService,
private config: AuthClientConfig,
) {}
logout() {
const domain = this.config.get().domain;
const packageId = 'com.example.myapp'; // from capacitor.config.ts
const returnTo = `${packageId}://${domain}/capacitor/${packageId}/callback`;
this.auth
.logout({
logoutParams: { returnTo },
async openUrl(url: string) {
await Browser.open({ url, windowName: '_self' });
},
})
.subscribe();
}
}User Profile
Display User Info
import { Component } from '@angular/core';
import { AuthService } from '@auth0/auth0-angular';
import { AsyncPipe, NgIf } from '@angular/common';
import { IonCard, IonCardHeader, IonCardTitle, IonCardContent, IonAvatar } from '@ionic/angular/standalone';
@Component({
selector: 'app-profile',
standalone: true,
imports: [AsyncPipe, NgIf, IonCard, IonCardHeader, IonCardTitle, IonCardContent, IonAvatar],
template: `
<ion-card *ngIf="auth.user$ | async as user">
<ion-card-header>
<ion-avatar>
<img [src]="user.picture" [alt]="user.name" />
</ion-avatar>
<ion-card-title>{{ user.name }}</ion-card-title>
</ion-card-header>
<ion-card-content>
<p>{{ user.email }}</p>
</ion-card-content>
</ion-card>
`,
})
export class ProfileComponent {
constructor(public auth: AuthService) {}
}Access ID Token Claims
import { Component } from '@angular/core';
import { AuthService } from '@auth0/auth0-angular';
@Component({
selector: 'app-claims',
template: `
<pre *ngIf="auth.idTokenClaims$ | async as claims">
{{ claims | json }}
</pre>
`,
})
export class ClaimsComponent {
constructor(public auth: AuthService) {}
}Token Management
Get Access Token
import { Component } from '@angular/core';
import { AuthService } from '@auth0/auth0-angular';
@Component({ ... })
export class ApiComponent {
constructor(private auth: AuthService, private http: HttpClient) {}
callApi() {
this.auth.getAccessTokenSilently().subscribe(token => {
this.http.get('https://my-api.example.com/data', {
headers: { Authorization: `Bearer ${token}` },
}).subscribe(data => console.log(data));
});
}
}Use HTTP Interceptor (Recommended)
The authHttpInterceptorFn automatically attaches tokens to matching requests:
// app.config.ts
import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { provideAuth0, authHttpInterceptorFn } from '@auth0/auth0-angular';
export const appConfig: ApplicationConfig = {
providers: [
provideHttpClient(withInterceptors([authHttpInterceptorFn])),
provideAuth0({
domain: 'YOUR_AUTH0_DOMAIN',
clientId: 'YOUR_AUTH0_CLIENT_ID',
useRefreshTokens: true,
useRefreshTokensFallback: false,
authorizationParams: {
audience: 'https://my-api.example.com',
},
httpInterceptor: {
allowedList: ['https://my-api.example.com/*'],
},
}),
],
};Then make HTTP calls as normal — tokens are added automatically:
this.http.get('https://my-api.example.com/data').subscribe(data => {
console.log(data);
});Route Guards
Protect Routes with authGuardFn
import { Routes } from '@angular/router';
import { authGuardFn } from '@auth0/auth0-angular';
export const routes: Routes = [
{ path: '', component: HomePage },
{ path: 'profile', component: ProfilePage, canActivate: [authGuardFn] },
{ path: 'settings', component: SettingsPage, canActivate: [authGuardFn] },
];When an unauthenticated user navigates to a protected route, authGuardFn automatically triggers loginWithRedirect().
Error Handling
Subscribe to Auth Errors
import { Component, OnInit } from '@angular/core';
import { AuthService } from '@auth0/auth0-angular';
@Component({ ... })
export class AppComponent implements OnInit {
constructor(private auth: AuthService) {}
ngOnInit() {
this.auth.error$.subscribe(error => {
if (error) {
console.error('Auth error:', error.message);
// Show toast or navigate to error page
}
});
}
}Handle Callback Errors
CapApp.addListener('appUrlOpen', ({ url }) => {
this.ngZone.run(() => {
if (url.includes('state') && (url.includes('code') || url.includes('error'))) {
this.auth.handleRedirectCallback(url).pipe(
mergeMap(() => Browser.close()),
).subscribe({
error: (err) => {
console.error('Callback error:', err);
Browser.close();
},
});
}
});
});Capacitor Lifecycle Considerations
Listener Cleanup
If registering the appUrlOpen listener in a component that can be destroyed (not AppComponent), clean up:
import { Component, NgZone, OnInit, OnDestroy } from '@angular/core';
import { App as CapApp } from '@capacitor/app';
import { PluginListenerHandle } from '@capacitor/core';
@Component({ ... })
export class AuthCallbackComponent implements OnInit, OnDestroy {
private listenerHandle?: PluginListenerHandle;
async ngOnInit() {
this.listenerHandle = await CapApp.addListener('appUrlOpen', ({ url }) => {
this.ngZone.run(() => {
// handle callback...
});
});
}
async ngOnDestroy() {
await this.listenerHandle?.remove();
}
}App Resume / Background
The Auth0 Angular SDK handles token refresh automatically via useRefreshTokens: true. When the app resumes from background:
- If the refresh token is still valid,
getAccessTokenSilently()returns a fresh access token - If the refresh token has expired,
isAuthenticated$will emitfalseand the user needs to log in again
Testing Patterns
Mock AuthService in Unit Tests
import { TestBed } from '@angular/core/testing';
import { AuthService } from '@auth0/auth0-angular';
import { of } from 'rxjs';
const mockAuthService = {
isAuthenticated$: of(true),
user$: of({ name: 'Test User', email: 'test@example.com', picture: 'https://example.com/pic.jpg' }),
loginWithRedirect: jasmine.createSpy('loginWithRedirect').and.returnValue(of(void 0)),
logout: jasmine.createSpy('logout').and.returnValue(of(void 0)),
getAccessTokenSilently: jasmine.createSpy('getAccessTokenSilently').and.returnValue(of('mock-token')),
};
TestBed.configureTestingModule({
providers: [
{ provide: AuthService, useValue: mockAuthService },
],
});auth0-ionic-angular — Setup Guide
Auth0 Configuration
Agent instruction:
>
SECURITY — Never display credentials:
After obtaining Auth0 credentials (domain, client ID) — whether from the Auth0 CLI or a user-provided env file — NEVER print, echo, or display them in your text output. Write them directly to the config file (src/environments/environment.ts) silently. Do NOT produce output like "Domain: xxx" or "Client ID: yyy". Instead, confirm that the config file has been written and tell the user where to find it.>
Always present the setup choice:
Regardless of whether the user has already provided credentials in their prompt, always use AskUserQuestion to let the developer choose between Automatic and Manual setup:>
```
AskUserQuestion:
question: "How would you like to configure Auth0 for your Ionic Angular app?"
options:
- label: "Automatic Setup (Recommended)"
description: "Uses the Auth0 CLI to create a Native application, configure callback URLs, and store credentials in your project automatically."
- label: "Manual Setup"
description: "You provide an .env file with your Auth0 Domain and Client ID, and the agent reads it and writes the project configuration for you."
```
---
Option A: Automatic Setup (Auth0 CLI)
The agent executes Auth0 CLI commands to create the application, configure it, retrieve credentials, and write them to the project config file — fully hands-free.
Step A1: Pre-flight checks
Run these checks in order. If any fail, guide the user to fix the issue or fall back to Manual Setup.
# Verify Node.js 20+
node --version
# Verify Auth0 CLI is installed
auth0 --version --no-input
# Verify logged in to Auth0
auth0 tenants list --csv --no-inputIf the Auth0 CLI is not installed, instruct the user:
# macOS
brew install auth0/auth0-cli/auth0
# Linux
curl -sSfL https://raw.githubusercontent.com/auth0/auth0-cli/main/install.sh | shIf not logged in:
auth0 loginStep A2: Detect project and appId
- Verify
package.jsoncontains@angular/core,@ionic/angular, and@capacitor/core - Read
appIdfromcapacitor.config.ts(matchappId: 'com.example.app') orcapacitor.config.json - If neither config file exists or
appIdis not found, usecom.example.appas default and warn the user
Step A3: Get the active tenant domain
auth0 tenants list --csv --no-inputParse the output to find the line containing → — the second CSV column on that line is the active domain.
Step A4: Create a Native Auth0 application
auth0 apps create \
--name "PROJECT_NAME-ionic-angular" \
--type native \
--auth-method none \
--callbacks "PACKAGE_ID://DOMAIN/capacitor/PACKAGE_ID/callback" \
--logout-urls "PACKAGE_ID://DOMAIN/capacitor/PACKAGE_ID/callback" \
--origins "capacitor://localhost,http://localhost" \
--json --no-inputReplace PROJECT_NAME with the project name from package.json, PACKAGE_ID with the appId from Step A2, and DOMAIN with the tenant domain from Step A3.
Extract client_id from the JSON output.
Step A5: Enable Username-Password-Authentication connection
auth0 api get connectionsParse the JSON array to find the connection with "name": "Username-Password-Authentication".
- If it exists but doesn't include the new
client_idinenabled_clients, update it:
auth0 api patch "connections/CONNECTION_ID" --data '{"enabled_clients":["EXISTING_ID_1","EXISTING_ID_2","NEW_CLIENT_ID"]}'Keep all existing enabled_clients and append the new one.
- If it doesn't exist, create it:
auth0 api post connections --data '{"strategy":"auth0","name":"Username-Password-Authentication","enabled_clients":["CLIENT_ID"]}'- If it already includes the client_id, skip this step.
Step A6: Write config file
Create src/environments/ directory if it doesn't exist, then write src/environments/environment.ts:
export const environment = {
production: false,
auth0: {
domain: 'DOMAIN',
clientId: 'CLIENT_ID',
callbackUrl: 'PACKAGE_ID://DOMAIN/capacitor/PACKAGE_ID/callback',
appId: 'PACKAGE_ID',
},
};Step A7: Confirm completion
Tell the user that Auth0 has been configured and credentials have been written to src/environments/environment.ts. Do NOT display the domain, client ID, or any credential values in the output.
---
Option B: Manual Setup
The developer provides an .env file containing their Auth0 credentials. The agent reads the file, extracts the values, and writes the project configuration.
Step B1: Ask for the env file path
Use AskUserQuestion to ask the developer for the path to their .env file:
AskUserQuestion:
question: "Please provide the path to your .env file containing Auth0 credentials (AUTH0_DOMAIN and AUTH0_CLIENT_ID):"The .env file should contain lines in this format:
AUTH0_DOMAIN=your-tenant.auth0.com
AUTH0_CLIENT_ID=your_client_id_hereAgent instruction: Read the file at the path the user provides. Extract the values forAUTH0_DOMAINandAUTH0_CLIENT_IDby parsingKEY=VALUElines. If the file is missing either key, useAskUserQuestionto ask the user to provide the missing value. Accept common variations:DOMAIN/AUTH0_DOMAIN,CLIENT_ID/AUTH0_CLIENT_ID.
Step B2: Detect appId
Read appId from capacitor.config.ts (match appId: 'com.example.app') or capacitor.config.json. If not found, use com.example.app as default and warn the user.
Step B3: Write config file
Create src/environments/ directory if it doesn't exist, then write src/environments/environment.ts:
export const environment = {
production: false,
auth0: {
domain: 'DOMAIN',
clientId: 'CLIENT_ID',
callbackUrl: 'PACKAGE_ID://DOMAIN/capacitor/PACKAGE_ID/callback',
appId: 'PACKAGE_ID',
},
};Step B4: Remind user to configure Auth0 Dashboard
Since credentials were provided manually, the user must also configure the Auth0 Dashboard themselves. Display these required settings:
| Setting | Value |
|---|---|
| Application Type | Native |
| Allowed Callback URLs | PACKAGE_ID://DOMAIN/capacitor/PACKAGE_ID/callback |
| Allowed Logout URLs | PACKAGE_ID://DOMAIN/capacitor/PACKAGE_ID/callback |
| Allowed Origins | capacitor://localhost, http://localhost |
Also add http://localhost:8100 to Callback URLs, Logout URLs, and Web Origins if the user will use ionic serve for local development.
No Client Secret is needed — Native apps use PKCE.
Auth0 Dashboard Configuration
Create a Native Application
1. Go to Auth0 Dashboard → Applications → Create Application 2. Select Native as the application type 3. Note the Domain and Client ID from the Settings tab
Configure URLs
Determine your appId from capacitor.config.ts (e.g., com.example.myapp).
| Setting | Value |
|---|---|
| Allowed Callback URLs | PACKAGE_ID://YOUR_DOMAIN/capacitor/PACKAGE_ID/callback |
| Allowed Logout URLs | PACKAGE_ID://YOUR_DOMAIN/capacitor/PACKAGE_ID/callback |
| Allowed Origins | capacitor://localhost, http://localhost |
Example with appId = com.example.myapp and domain dev-abc123.us.auth0.com:
com.example.myapp://dev-abc123.us.auth0.com/capacitor/com.example.myapp/callbackSDK Installation
npm install @auth0/auth0-angular @capacitor/browser @capacitor/appIf Capacitor platforms aren't added yet:
npx cap add ios
npx cap add androidSDK Configuration
Standalone Components (Angular 17+)
In src/app/app.config.ts:
import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideAuth0 } from '@auth0/auth0-angular';
import { routes } from './app.routes';
// Replace with your capacitor.config.ts appId and Auth0 domain
const appId = 'YOUR_PACKAGE_ID';
const domain = 'YOUR_AUTH0_DOMAIN';
const callbackUri = `${appId}://${domain}/capacitor/${appId}/callback`;
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes),
provideAuth0({
domain,
clientId: 'YOUR_AUTH0_CLIENT_ID',
useRefreshTokens: true,
useRefreshTokensFallback: false,
authorizationParams: {
redirect_uri: callbackUri,
},
}),
],
};NgModule (Angular 16 and earlier)
In src/app/app.module.ts:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { IonicModule } from '@ionic/angular';
import { AuthModule } from '@auth0/auth0-angular';
import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';
const appId = 'YOUR_PACKAGE_ID';
const domain = 'YOUR_AUTH0_DOMAIN';
const callbackUri = `${appId}://${domain}/capacitor/${appId}/callback`;
@NgModule({
declarations: [AppComponent],
imports: [
BrowserModule,
IonicModule.forRoot(),
AppRoutingModule,
AuthModule.forRoot({
domain,
clientId: 'YOUR_AUTH0_CLIENT_ID',
useRefreshTokens: true,
useRefreshTokensFallback: false,
authorizationParams: {
redirect_uri: callbackUri,
},
}),
],
bootstrap: [AppComponent],
})
export class AppModule {}Post-Setup: Deep Linking Configuration
iOS
The custom URL scheme is automatically registered by Capacitor from capacitor.config.ts. Verify in ios/App/App/Info.plist:
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>PACKAGE_ID</string>
</array>
</dict>
</array>Android
Verify the intent filter in android/app/src/main/AndroidManifest.xml:
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="PACKAGE_ID" />
</intent-filter>Secret Management
- No Client Secret needed — Ionic Capacitor apps are Native apps that use PKCE for authentication
- Never embed secrets in client-side code — the Auth0 Angular SDK only requires
domainandclientId - Configuration values (domain, clientId) can be hardcoded in
app.config.ts/app.module.tsor loaded fromenvironment.ts
Using environment.ts (optional)
// src/environments/environment.ts
export const environment = {
production: false,
auth0: {
domain: 'YOUR_AUTH0_DOMAIN',
clientId: 'YOUR_AUTH0_CLIENT_ID',
},
};// src/app/app.config.ts
import { environment } from '../environments/environment';
const appId = 'YOUR_PACKAGE_ID'; // from capacitor.config.ts
const callbackUri = `${appId}://${environment.auth0.domain}/capacitor/${appId}/callback`;
provideAuth0({
domain: environment.auth0.domain,
clientId: environment.auth0.clientId,
useRefreshTokens: true,
useRefreshTokensFallback: false,
authorizationParams: {
redirect_uri: callbackUri,
},
}),Verification
After setup, verify:
1. Build succeeds: npm run build 2. Capacitor sync: npx cap sync 3. Run on device/emulator:
- iOS:
npx cap open ios→ Run in Xcode - Android:
npx cap open android→ Run in Android Studio
4. Login opens system browser (not in-app WebView) 5. Callback returns to app with user profile
{
"metadata": {
"skill_name": "auth0-ionic-angular",
"sdk_type": "WEB_SPA",
"framework": "Ionic Angular",
"language": "TypeScript",
"package": "@auth0/auth0-angular",
"generated_by": "quickstart-skill-generator"
},
"configurations": {
"baseline": {
"name": "Baseline (no tools)",
"description": "Single LLM call, no tools, no skill — pure training data knowledge",
"skill_context": "none",
"tools": "none"
},
"without_skill": {
"name": "Agent Only",
"description": "Agent has tools but no skill context",
"skill_context": "none",
"tools": "full"
},
"with_skill": {
"name": "Agent + Skill",
"description": "Agent has tools and generated SKILL.md in context",
"skill_context": "full",
"tools": "full"
}
},
"scaffold": {
"sdk_type": "WEB_SPA",
"framework": "Ionic Angular",
"language": "TypeScript"
},
"scoring": {
"dimensions": {
"correctness": 0.20,
"hallucination": 0.15,
"security": 0.15,
"skill_differentiation": 0.30,
"semantic": 0.20
},
"thresholds": {
"skill_valuable": { "delta_gte": 0.30 },
"skill_acceptable": { "with_skill_pass_rate_gte": 0.85 },
"needs_improvement": { "delta_lt": 0.15 }
}
}
}
{
"skill_name": "auth0-ionic-angular",
"evals": [
{
"id": 1,
"prompt": "Add Auth0 authentication to an Ionic Angular application using the @auth0/auth0-angular SDK with Capacitor.\n\n**Auth0 Credentials:**\n- Domain: `dev-example.auth0.com`\n- Client ID: `abc123def456ghi789jkl012`",
"expected_output": "Working Ionic Angular Capacitor app with Auth0 provider, login/logout via system browser, deep link callback handling, and user profile display",
"expectations": [
"Auth0 Angular SDK installed via npm",
"Has correct import or dependency for @auth0/auth0-angular",
"Auth0 SDK initialized (provideAuth0 or AuthModule.forRoot configured)",
"Has Auth0 provider/wrapper component configured",
"Implements login functionality",
"Implements logout functionality",
"Auth0 domain dev-example.auth0.com written to environment.ts config file",
"Client ID abc123def456ghi789jkl012 written to environment.ts config file",
"Does not use server or native SDK in Ionic Angular app",
"No client secret in Ionic Angular code (Native app uses PKCE)",
"Does not store tokens in localStorage (insecure on mobile)",
"Solution correctly integrates Auth0 into Ionic Angular Capacitor app with provider, login/logout using Browser.open, and deep link callback handling",
"Uses a specific SDK version in dependency declaration",
"Configures refresh token rotation (useRefreshTokens: true) for mobile",
"Disables refresh token fallback (useRefreshTokensFallback: false) for mobile",
"Uses NgZone.run() for Capacitor callback handling",
"Uses Capacitor Browser plugin for system browser authentication",
"Uses Capacitor App plugin for deep link callback handling",
"Calls handleRedirectCallback to process deep link auth response",
"Contains at least 2 Ionic Capacitor-specific advanced patterns (useRefreshTokens, ngZone.run, Browser.open, appUrlOpen)"
]
}
]
}
[
{"type": "matches", "pattern": "npm install.*@auth0/auth0-angular|@auth0/auth0-angular.*\\d+\\.\\d+", "description": "Auth0 Angular SDK installed via npm"},
{"type": "contains", "value": "@auth0/auth0-angular", "description": "Has correct import or dependency for @auth0/auth0-angular"},
{"type": "contains_any", "values": ["provideAuth0", "AuthModule.forRoot"], "description": "Auth0 SDK initialized (provideAuth0 or AuthModule.forRoot configured)"},
{"type": "matches", "pattern": "provideAuth0|AuthModule\\.forRoot|Auth0Provider", "description": "Has Auth0 provider/wrapper component configured"},
{"type": "matches", "pattern": "loginWithRedirect|login\\(", "description": "Implements login functionality"},
{"type": "matches", "pattern": "logout\\(|auth\\.logout", "description": "Implements logout functionality"},
{"type": "file_contains", "file_pattern": "**/environment*.ts", "value": "dev-example.auth0.com", "description": "Auth0 domain written to correct config file (environment.ts)"},
{"type": "file_contains", "file_pattern": "**/environment*.ts", "value": "abc123def456ghi789jkl012", "description": "Client ID written to correct config file (environment.ts)"},
{"type": "not_contains_any", "values": ["@auth0/nextjs-auth0", "express-openid-connect", "auth0-react-native"], "description": "Does not use server or native SDK in Ionic Angular app"},
{"type": "not_contains", "value": "client_secret", "description": "No client secret in Ionic Angular code (Native app uses PKCE)"},
{"type": "not_contains", "value": "localStorage", "description": "Does not store tokens in localStorage (insecure on mobile)"},
{"type": "judge", "description": "Does the solution correctly integrate Auth0 into an Ionic Angular Capacitor app", "question": "Does the solution correctly integrate Auth0 into an Ionic Angular Capacitor app with Auth0 provider (provideAuth0 or AuthModule.forRoot), login/logout using Browser.open, and deep link callback handling?", "examples": "PASS: Uses provideAuth0 or AuthModule.forRoot, loginWithRedirect with Browser.open, appUrlOpen listener with handleRedirectCallback in ngZone.run.\nFAIL: Uses server-side session instead of SPA token flow.\nFAIL: Uses window.location.href instead of Browser.open for login.", "framework": "ionic-angular"},
{"type": "matches", "pattern": "\\d+\\.\\d+\\.\\d+", "description": "Uses a specific SDK version in dependency declaration"},
{"type": "matches", "pattern": "useRefreshTokens\\s*:\\s*true", "description": "Configures refresh token rotation (useRefreshTokens: true) — required for mobile"},
{"type": "matches", "pattern": "useRefreshTokensFallback\\s*:\\s*false", "description": "Disables refresh token fallback (useRefreshTokensFallback: false) — required for mobile"},
{"type": "matches", "pattern": "ngZone\\.run|NgZone", "description": "Uses NgZone.run() for Capacitor callback handling (Angular change detection)"},
{"type": "matches", "pattern": "Browser\\.open|@capacitor/browser", "description": "Uses Capacitor Browser plugin for system browser authentication"},
{"type": "matches", "pattern": "appUrlOpen|@capacitor/app", "description": "Uses Capacitor App plugin for deep link callback handling"},
{"type": "matches", "pattern": "handleRedirectCallback", "description": "Calls handleRedirectCallback to process deep link auth response"},
{"type": "judge", "description": "Contains skill-specific advanced pattern for Ionic Capacitor", "question": "Check ONLY for these skill-specific advanced patterns (NOT basic SDK usage like loginWithRedirect, logout, isAuthenticated, user$, AuthService). Look for at least 2 of: (1) useRefreshTokens: true AND useRefreshTokensFallback: false, (2) ngZone.run() wrapping Capacitor callbacks, (3) Browser.open instead of default redirect, (4) appUrlOpen listener with handleRedirectCallback. These are Ionic Capacitor-specific patterns taught by the skill that are NOT part of standard Angular SPA quickstart knowledge. Answer YES only if at least 2 of these advanced patterns are present, NO otherwise.", "examples": "PASS: Uses useRefreshTokens: true AND wraps handleRedirectCallback in ngZone.run().\nFAIL: Only uses basic provideAuth0 with loginWithRedirect, no Capacitor-specific patterns.", "framework": "ionic-angular"}
]
/**
* Compatibility shim for auth0-evals.
*
* Reads graders.json (rich format with custom types, tier, description)
* and exports defineGraders() returning auth0-evals GraderDef[] format.
*
* Custom type mappings:
* file_contains → contains (loses file-specificity)
* contains_any → contains (first value only)
* not_contains_any → not_contains (first value only)
* all → flattened sub-graders
* judge.examples → stripped
*/
import { readFileSync } from "fs";
import { join, dirname } from "path";
import { fileURLToPath } from "url";
const __dirname = dirname(fileURLToPath(import.meta.url));
/** auth0-evals GraderDef — the format runGraders() expects */
export interface GraderDef {
kind: string;
name: string;
needle?: string;
pattern?: string;
question?: string;
framework?: string;
}
/** Rich grader from graders.json (superset of auth0-evals types) */
interface RichGrader {
type: string;
value?: string;
values?: string[];
pattern?: string;
description?: string;
question?: string;
examples?: string;
framework?: string;
file_pattern?: string;
tier?: number;
graders?: RichGrader[];
}
function mapGrader(g: RichGrader): GraderDef | GraderDef[] {
const name = g.description ?? "";
switch (g.type) {
case "contains":
return { kind: "contains", needle: g.value, name };
case "file_contains":
return { kind: "contains", needle: g.value, name };
case "contains_any":
return { kind: "contains", needle: g.values?.[0], name };
case "not_contains":
return { kind: "not_contains", needle: g.value, name };
case "not_contains_any":
return { kind: "not_contains", needle: g.values?.[0], name };
case "matches":
return { kind: "matches", pattern: g.pattern, name };
case "all":
return (g.graders ?? []).flatMap((sub) => {
const mapped = mapGrader(sub);
return Array.isArray(mapped) ? mapped : [mapped];
});
case "judge":
return {
kind: "judge",
question: g.question,
framework: g.framework,
name: g.question?.slice(0, 80) ?? name,
};
default:
return { kind: g.type, name };
}
}
/**
* Reads graders.json and returns auth0-evals compatible GraderDef[].
* Custom types are mapped to standard primitives (contains, not_contains, matches, judge).
*/
export function defineGraders(): GraderDef[] {
const raw: RichGrader[] = JSON.parse(
readFileSync(join(__dirname, "graders.json"), "utf-8")
);
return raw.flatMap((g) => {
const mapped = mapGrader(g);
return Array.isArray(mapped) ? mapped : [mapped];
});
}
{
"name": "auth0-ionic-angular-evals",
"version": "1.0.0",
"description": "Eval runner for auth0-ionic-angular skill",
"type": "module",
"scripts": {
"eval": "node run-evals.mjs",
"eval:3x": "node run-evals.mjs --runs 3",
"eval:sequential": "node run-evals.mjs --sequential",
"eval:grade-only": "node run-evals.mjs --grade-only"
},
"dependencies": {
"execa": "^9.0.0",
"ora": "^8.0.0"
}
}
Agent System
You are a software developer adding Auth0 authentication to an Ionic Angular application with Capacitor. You have access to tools for reading/writing files, running commands, and fetching URLs. Use these tools to complete the integration task below.
Task
Add Auth0 authentication to an Ionic Angular application using the @auth0/auth0-angular SDK with Capacitor.
Auth0 Credentials:
- Domain:
dev-example.auth0.com - Client ID:
abc123def456ghi789jkl012