
Auth0 Ionic Vue Skill
- 292 installs
- 39 repo stars
- auth0/agent-skills
Set up Auth0 authentication for Ionic Vue mobile applications with composables.
About
Auth0 Ionic Vue integration skill brings authentication to Ionic Vue projects. Vue developers use it to add secure user login with minimal boilerplate.
- Ionic Vue
- Auth0 SDK
- Composables
Auth0 Ionic Vue by the numbers
- 292 all-time installs (skills.sh)
- Ranked #376 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-vueAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 292 |
|---|---|
| repo stars | ★ 39 |
| Repository | auth0/agent-skills ↗ |
What it does
Set up Auth0 authentication for Ionic Vue mobile applications with composables.
Files
Auth0 Ionic Vue (Capacitor) Integration
Add Auth0 authentication to Ionic Vue applications using Capacitor. This skill covers native mobile authentication using the @auth0/auth0-vue SDK combined with @capacitor/browser and @capacitor/app plugins for deep link handling on iOS and Android.
Prerequisites
- Node.js 18+
- Ionic CLI (
npm install -g @ionic/cli) - An existing Ionic Vue application with Capacitor configured
- Auth0 account and tenant
- For iOS: Xcode 14+ and CocoaPods
- For Android: Android Studio with API level 21+
- Auth0 CLI —
brew install auth0/auth0-cli/auth0
When NOT to Use
| Use Case | Recommended Skill |
|---|---|
| Vue SPA (no Capacitor/Ionic) | auth0-vue |
| React SPA (no Capacitor/Ionic) | auth0-react |
| React Native (bare CLI) | auth0-react-native |
| Expo (React Native) | auth0-expo |
| Ionic + React + Capacitor | auth0-ionic-react |
| Ionic + Angular + Capacitor | auth0-ionic-angular |
| Next.js (server-side) | auth0-nextjs |
| Nuxt (server-side) | auth0-nuxt |
| iOS native (Swift) | auth0-swift |
| Android native (Kotlin) | auth0-android |
Quick Start Workflow
Step 1: Configure Auth0
For automated setup with Auth0 CLI, see Setup Guide for complete scripts.
For manual setup, configure a Native application in the Auth0 Dashboard and note your Domain and Client ID.
Step 2: Install Dependencies
npm install @auth0/auth0-vue @capacitor/browser @capacitor/app
npx cap syncStep 3: Set Up Auth0 Plugin
Agent instruction: If Step 1 already wrote.envand updatedsrc/main.ts, verify the configuration looks correct and skip to Step 4. Only use the template below if configuring manually.
Register the Auth0 Vue plugin in your app entry point (src/main.ts), configuring it for Capacitor. Credentials are read from .env via import.meta.env:
import { createApp } from 'vue';
import { createAuth0 } from '@auth0/auth0-vue';
import { IonicVue } from '@ionic/vue';
import App from './App.vue';
import router from './router';
// Agent: read appId from capacitor.config.ts and replace this value
const packageId = "YOUR_PACKAGE_ID";
const app = createApp(App);
app.use(IonicVue);
app.use(router);
app.use(
createAuth0({
domain: import.meta.env.VITE_AUTH0_DOMAIN,
clientId: import.meta.env.VITE_AUTH0_CLIENT_ID,
useRefreshTokens: true,
useRefreshTokensFallback: false,
authorizationParams: {
redirect_uri: `${packageId}://${import.meta.env.VITE_AUTH0_DOMAIN}/capacitor/${packageId}/callback`
}
})
);
router.isReady().then(() => {
app.mount('#app');
});Step 4: Implement Login with Capacitor Browser
<script setup lang="ts">
import { useAuth0 } from '@auth0/auth0-vue';
import { Browser } from '@capacitor/browser';
import { IonButton } from '@ionic/vue';
const { loginWithRedirect } = useAuth0();
const login = async () => {
await loginWithRedirect({
async openUrl(url: string) {
await Browser.open({ url, windowName: "_self" });
}
});
};
</script>
<template>
<ion-button @click="login">Log in</ion-button>
</template>Step 5: Handle Callback via Deep Link
Handle the deep link callback in your App.vue component. This must run on app initialization:
<script setup lang="ts">
import { onMounted, onUnmounted } from 'vue';
import { useAuth0 } from '@auth0/auth0-vue';
import { App as CapApp } from '@capacitor/app';
import { Browser } from '@capacitor/browser';
import { IonApp, IonRouterOutlet } from '@ionic/vue';
const { handleRedirectCallback } = useAuth0();
let urlOpenListener: any;
onMounted(async () => {
urlOpenListener = await CapApp.addListener('appUrlOpen', async ({ url }) => {
if (url.includes('state') && (url.includes('code') || url.includes('error'))) {
await handleRedirectCallback(url);
}
await Browser.close();
});
});
onUnmounted(() => {
urlOpenListener?.remove();
});
</script>
<template>
<ion-app>
<ion-router-outlet />
</ion-app>
</template>Step 6: Implement Logout
<script setup lang="ts">
import { useAuth0 } from '@auth0/auth0-vue';
import { Browser } from '@capacitor/browser';
import { IonButton } from '@ionic/vue';
const domain = import.meta.env.VITE_AUTH0_DOMAIN;
// Agent: read appId from capacitor.config.ts and replace this value
const packageId = "YOUR_PACKAGE_ID";
const logoutUri = `${packageId}://${domain}/capacitor/${packageId}/callback`;
const { logout } = useAuth0();
const doLogout = async () => {
await logout({
logoutParams: {
returnTo: logoutUri
},
async openUrl(url: string) {
await Browser.open({ url, windowName: "_self" });
}
});
};
</script>
<template>
<ion-button @click="doLogout">Log out</ion-button>
</template>Step 7: Build and Test
Agent instruction: After integration, verify the build:
```bash
ionic build
npx cap sync
```
For iOS: npx cap open ios then build in Xcode.For Android: npx cap open android then build in Android Studio.If the build fails, iterate up to 5-6 times to fix issues. If still failing, use AskUserQuestion to request help.Detailed Documentation
- [Setup Guide](./references/setup.md) — Auth0 CLI automated setup (login, app creation, credential injection), Capacitor URL scheme registration, secret management
- [Integration Patterns](./references/integration.md) — Login/logout with Capacitor Browser, deep link callback handling, user profile, protected routes, token access, error handling
- [Testing & Reference](./references/api.md) — Full API reference for createAuth0 options, useAuth0 composable, Capacitor plugin configuration, testing checklist, common issues
Common Mistakes
| Mistake | Fix |
|---|---|
| App type not set to Native in Auth0 Dashboard | Change application type to "Native" in Dashboard settings |
| Missing or incorrect callback URL format | Use YOUR_PACKAGE_ID://YOUR_DOMAIN/capacitor/YOUR_PACKAGE_ID/callback — must match exactly |
| Not enabling refresh tokens | Set useRefreshTokens: true and useRefreshTokensFallback: false in createAuth0() |
Missing @capacitor/browser or @capacitor/app | Install both: npm install @capacitor/browser @capacitor/app && npx cap sync |
| Not handling deep link callback | Add CapApp.addListener('appUrlOpen', ...) to process Auth0 redirect |
Forgetting npx cap sync after install | Always run npx cap sync after installing Capacitor plugins |
Using window.location.origin as redirect URI | Use the custom URL scheme (packageId://domain/...), not http://localhost |
| Missing Allowed Origins in Dashboard | Add capacitor://localhost, http://localhost to Allowed Origins |
Not calling app.use(createAuth0(...)) before mount | Register Auth0 plugin before calling app.mount('#app') |
Accessing .value incorrectly on auth refs | useAuth0() returns Vue refs — use .value in <script>, template unwraps automatically |
| localStorage treated as persistent on mobile | Use refresh tokens (useRefreshTokens: true) for reliable token persistence |
WebAuth Method
This SDK uses Auth0's Universal Login (WebAuth) via the Capacitor Browser plugin. The loginWithRedirect() method opens the Auth0 authorization endpoint in a system browser (SFSafariViewController on iOS, Chrome Custom Tabs on Android). After authentication, Auth0 redirects back to the app using a native callback URL with a custom scheme: {packageId}://{domain}/capacitor/{packageId}/callback. The @capacitor/app plugin captures this deep link, and handleRedirectCallback(url) processes the authorization code exchange.
Unlike standard native SDKs that use https://{domain}/android/{packageId}/callback or https://{domain}/ios/{bundleId}/callback, Ionic Capacitor apps use the Capacitor-specific callback path with the package ID as the URL scheme.
Related Skills
- auth0-vue — Vue SPA (browser-only, no Capacitor)
- auth0-ionic-react — Ionic with React and Capacitor
- auth0-ionic-angular — Ionic with Angular and Capacitor
- auth0-react-native — React Native (bare CLI, no Ionic/Capacitor)
- auth0-expo — Expo (React Native) with Auth0
Quick Reference
| API | Description |
|---|---|
createAuth0(options) | Vue plugin factory — registers Auth0 with app.use() |
useAuth0() | Composable — returns { isLoading, isAuthenticated, user, loginWithRedirect, logout, getAccessTokenSilently, handleRedirectCallback, error } |
loginWithRedirect({ openUrl }) | Login via Universal Login — use Browser.open() in openUrl callback |
logout({ logoutParams, openUrl }) | Logout — use Browser.open() in openUrl callback |
handleRedirectCallback(url) | Process Auth0 callback URL from deep link |
getAccessTokenSilently() | Get access token (uses refresh tokens on mobile) |
createAuthGuard(app) | Vue Router navigation guard factory for protected routes |
Browser.open({ url }) | Capacitor — opens URL in system browser (SFSafariViewController / Chrome Custom Tabs) |
CapApp.addListener('appUrlOpen', cb) | Capacitor — listens for deep link events |
Browser.close() | Capacitor — closes the in-app browser after callback |
References
Auth0 Ionic Vue (Capacitor) — API Reference & Testing
createAuth0 Configuration
| 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 (for Capacitor) | false | Must be true for native mobile — uses refresh tokens instead of iframe |
useRefreshTokensFallback | boolean | Yes (for Capacitor) | true | Must be false for native mobile — disables iframe fallback |
authorizationParams.redirect_uri | string | Yes | — | Custom scheme callback URL: {packageId}://{domain}/capacitor/{packageId}/callback |
authorizationParams.audience | string | No | — | API identifier for access token audience |
authorizationParams.scope | string | No | openid profile email | OAuth scopes to request |
cacheLocation | string | No | memory | Token cache location: memory or localstorage |
Capacitor-Specific Configuration
app.use(
createAuth0({
domain: "your-tenant.auth0.com",
clientId: "your-client-id",
useRefreshTokens: true,
useRefreshTokensFallback: false,
authorizationParams: {
redirect_uri: "com.example.myapp://your-tenant.auth0.com/capacitor/com.example.myapp/callback"
}
})
);useAuth0() Composable
const {
isLoading, // Ref<boolean> — true while SDK initializes
isAuthenticated, // Ref<boolean> — true if user has valid session
user, // Ref<User | undefined> — authenticated user profile
error, // Ref<Error | undefined> — last authentication error
loginWithRedirect, // (options?) => Promise<void>
logout, // (options?) => Promise<void>
getAccessTokenSilently, // (options?) => Promise<string>
getAccessTokenWithPopup, // (options?) => Promise<string> (not for Capacitor)
handleRedirectCallback, // (url?) => Promise<RedirectLoginResult>
idTokenClaims, // Ref<IdToken | undefined> — raw ID token claims
checkSession, // () => Promise<void> — refresh authentication state
} = useAuth0();Note: All reactive properties (isLoading, isAuthenticated, user, error, idTokenClaims) are Vue Ref objects. Access their values with .value in <script> blocks; templates unwrap refs automatically.
loginWithRedirect Options (Capacitor)
await loginWithRedirect({
// Required for Capacitor: opens URL in system browser
async openUrl(url: string) {
await Browser.open({ url, windowName: "_self" });
},
// Optional: additional authorization params
authorizationParams: {
audience: "https://api.example.com/",
scope: "openid profile email read:data",
organization: "org_abc123",
invitation: "inv_xyz789",
}
});logout Options (Capacitor)
await logout({
logoutParams: {
returnTo: "com.example.myapp://your-tenant.auth0.com/capacitor/com.example.myapp/callback"
},
async openUrl(url: string) {
await Browser.open({ url, windowName: "_self" });
}
});getAccessTokenSilently Options
const token = await getAccessTokenSilently({
authorizationParams: {
audience: "https://api.example.com/",
scope: "read:data",
}
});
// Use token in API calls
const response = await fetch("https://api.example.com/data", {
headers: { Authorization: `Bearer ${token}` }
});createAuthGuard
Factory function that creates a Vue Router navigation guard for protected routes:
import { createAuthGuard } from '@auth0/auth0-vue';
import type { App } from 'vue';
// In router setup (needs app instance)
export function setupRouter(app: App) {
const router = createRouter({
history: createWebHistory(),
routes: [
{
path: '/profile',
component: () => import('../views/Profile.vue'),
beforeEnter: createAuthGuard(app)
}
]
});
return router;
}Claims Reference
| Claim | Source | Description |
|---|---|---|
sub | ID Token | User identifier (e.g., `auth0\ |
name | ID Token | User's full name |
email | ID Token | User's email address |
email_verified | ID Token | Whether email has been verified |
picture | ID Token | URL to user's profile picture |
nickname | ID Token | User's nickname |
updated_at | ID Token | Last profile update timestamp |
org_id | ID Token | Organization ID (when using Organizations) |
permissions | Access Token | RBAC permissions array (when API has RBAC enabled) |
Capacitor Plugin Configuration
capacitor.config.ts
import type { CapacitorConfig } from '@capacitor/cli';
const config: CapacitorConfig = {
appId: 'com.example.myapp',
appName: 'My App',
webDir: 'dist', // Ionic Vue with Vite uses 'dist'
server: {
androidScheme: 'https'
}
};
export default config;iOS: URL Scheme Registration
Add a custom URL scheme to ios/App/App/Info.plist so iOS can route the Auth0 callback deep link back to the app:
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>YOUR_PACKAGE_ID</string>
</array>
</dict>
</array>Replace YOUR_PACKAGE_ID with the appId from capacitor.config.ts (e.g., com.example.myapp).
Android: URL Scheme Registration
Add an intent filter to android/app/src/main/AndroidManifest.xml inside the main <activity> to handle the custom scheme callback:
<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="YOUR_PACKAGE_ID" />
</intent-filter>Replace YOUR_PACKAGE_ID with the appId from capacitor.config.ts. Ensure the appId matches the applicationId in android/app/build.gradle.
Auth0 Dashboard Configuration
Callback URLs
YOUR_PACKAGE_ID://YOUR_DOMAIN/capacitor/YOUR_PACKAGE_ID/callbackExample: com.example.myapp://your-tenant.auth0.com/capacitor/com.example.myapp/callback
Logout URLs
Same as callback URL:
YOUR_PACKAGE_ID://YOUR_DOMAIN/capacitor/YOUR_PACKAGE_ID/callbackAllowed Origins
capacitor://localhost, http://localhostTesting Checklist
- [ ] Auth0 plugin registered with
app.use(createAuth0({...}))with correct domain and clientId - [ ]
useRefreshTokens: trueanduseRefreshTokensFallback: falseare set - [ ]
redirect_uriuses custom scheme format (packageId://domain/capacitor/packageId/callback) - [ ] Login opens system browser (SFSafariViewController on iOS, Chrome Custom Tabs on Android)
- [ ] Deep link callback is handled via
CapApp.addListener('appUrlOpen', ...) - [ ]
handleRedirectCallback(url)is called when URL containsstateandcode/error - [ ]
Browser.close()is called after handling callback - [ ] Logout redirects back to app via custom scheme
- [ ]
getAccessTokenSilently()works with refresh tokens - [ ] User profile data is accessible via
useAuth0().user - [ ]
npx cap synchas been run after installing Capacitor plugins - [ ] Auth0 Dashboard has correct Callback URLs, Logout URLs, and Allowed Origins
- [ ] Application type is set to "Native" in Auth0 Dashboard
- [ ] Vue refs are accessed with
.valuein script, template unwraps automatically
Common Issues
| Issue | Cause | Solution |
|---|---|---|
| Login opens but never returns to app | Callback URL mismatch or missing deep link handler | Verify callback URL in Dashboard matches redirect_uri; ensure appUrlOpen listener is registered |
handleRedirectCallback not called | Deep link listener not set up or URL check is wrong | Verify CapApp.addListener('appUrlOpen', ...) runs in onMounted |
| Token refresh fails silently | useRefreshTokens not enabled | Set useRefreshTokens: true in createAuth0() |
| iframe fallback error on mobile | useRefreshTokensFallback not disabled | Set useRefreshTokensFallback: false in createAuth0() |
Browser.open does nothing | @capacitor/browser not installed or synced | Run npm install @capacitor/browser && npx cap sync |
| App crashes on deep link | Missing @capacitor/app plugin | Run npm install @capacitor/app && npx cap sync |
| CORS error during token exchange | Missing Allowed Origins in Auth0 Dashboard | Add capacitor://localhost, http://localhost to Allowed Origins |
user is undefined after login | Callback not processed before reading user | Wait for isLoading.value === false before accessing user.value |
| SSO not working on iOS | SFSafariViewController doesn't share Safari cookies (iOS 11+) | Expected limitation — SSO across apps is not supported on iOS |
| Auth plugin not found | createAuth0() not registered before mount | Call app.use(createAuth0({...})) before app.mount('#app') |
| Composable returns undefined | useAuth0() called outside setup or before plugin registration | Ensure useAuth0() is called inside <script setup> or setup() of a component |
Security Considerations
- No client secret: Native applications must not include a client secret. Use PKCE (Auth Code + PKCE) flow, which is the default.
- Refresh tokens: Always enable
useRefreshTokens: truefor Capacitor apps. localStorage is transient on mobile. - Token storage: The SDK stores tokens in memory by default. On mobile, refresh tokens are the reliable mechanism for session persistence.
- Custom scheme validation: The callback URL scheme must match the app's package/bundle ID exactly.
- HTTPS: Capacitor uses HTTPS for Android by default (
androidScheme: 'https'in config). Do not change this.
Integration Patterns
Authentication Flow
The Ionic Vue + Capacitor authentication flow:
1. User taps "Login" button 2. loginWithRedirect() is called with a custom openUrl that uses Browser.open() 3. Capacitor Browser opens Auth0 Universal Login in a system browser (SFSafariViewController on iOS, Chrome Custom Tabs on Android) 4. User authenticates with Auth0 5. Auth0 redirects to the custom scheme callback URL (packageId://domain/capacitor/packageId/callback) 6. Capacitor App plugin receives the deep link via appUrlOpen event 7. handleRedirectCallback(url) processes the authorization code 8. Browser.close() dismisses the system browser 9. User is now authenticated — isAuthenticated is true, user is populated
Auth0 Plugin Setup
Configure the Auth0 Vue plugin at your app's entry point (src/main.ts):
import { createApp } from 'vue';
import { createAuth0 } from '@auth0/auth0-vue';
import { IonicVue } from '@ionic/vue';
import App from './App.vue';
import router from './router';
const domain = "your-tenant.auth0.com";
const clientId = "your-client-id";
const packageId = "com.example.myapp";
const callbackUri = `${packageId}://${domain}/capacitor/${packageId}/callback`;
const app = createApp(App);
app.use(IonicVue);
app.use(router);
app.use(
createAuth0({
domain,
clientId,
useRefreshTokens: true,
useRefreshTokensFallback: false,
authorizationParams: {
redirect_uri: callbackUri
}
})
);
router.isReady().then(() => {
app.mount('#app');
});Why These Options Are Required for Capacitor
| Option | Value | Reason |
|---|---|---|
useRefreshTokens | true | Mobile apps cannot use iframe-based token renewal. Refresh tokens provide reliable session persistence. |
useRefreshTokensFallback | false | Prevents the SDK from attempting iframe fallback, which fails on native. |
authorizationParams.redirect_uri | Custom scheme URL | Native apps use a custom URL scheme, not http://localhost. |
Login Implementation
<script setup lang="ts">
import { useAuth0 } from '@auth0/auth0-vue';
import { Browser } from '@capacitor/browser';
import { IonButton } from '@ionic/vue';
const { loginWithRedirect } = useAuth0();
const login = async () => {
await loginWithRedirect({
async openUrl(url: string) {
await Browser.open({
url,
windowName: "_self"
});
}
});
};
</script>
<template>
<ion-button @click="login">Log in</ion-button>
</template>Deep Link Callback Handling
Handle the callback in your App.vue component. This must run on app initialization:
<script setup lang="ts">
import { onMounted, onUnmounted } from 'vue';
import { useAuth0 } from '@auth0/auth0-vue';
import { App as CapApp } from '@capacitor/app';
import { Browser } from '@capacitor/browser';
import { IonApp, IonRouterOutlet } from '@ionic/vue';
const { handleRedirectCallback } = useAuth0();
let urlOpenListener: any;
onMounted(async () => {
urlOpenListener = await CapApp.addListener('appUrlOpen', async ({ url }) => {
if (url.includes('state') && (url.includes('code') || url.includes('error'))) {
await handleRedirectCallback(url);
}
await Browser.close();
});
});
onUnmounted(() => {
urlOpenListener?.remove();
});
</script>
<template>
<ion-app>
<ion-router-outlet />
</ion-app>
</template>Logout Implementation
<script setup lang="ts">
import { useAuth0 } from '@auth0/auth0-vue';
import { Browser } from '@capacitor/browser';
import { IonButton } from '@ionic/vue';
const domain = "your-tenant.auth0.com";
const packageId = "com.example.myapp";
const logoutUri = `${packageId}://${domain}/capacitor/${packageId}/callback`;
const { logout } = useAuth0();
const doLogout = async () => {
await logout({
logoutParams: {
returnTo: logoutUri
},
async openUrl(url: string) {
await Browser.open({
url,
windowName: "_self"
});
}
});
};
</script>
<template>
<ion-button @click="doLogout">Log out</ion-button>
</template>User Profile Display
<script setup lang="ts">
import { useAuth0 } from '@auth0/auth0-vue';
import {
IonCard,
IonCardContent,
IonCardHeader,
IonCardTitle,
IonAvatar,
IonItem,
IonLabel,
IonSpinner
} from '@ionic/vue';
const { user, isLoading, isAuthenticated } = useAuth0();
</script>
<template>
<ion-spinner v-if="isLoading" />
<ion-card v-else-if="isAuthenticated && user">
<ion-card-header>
<ion-item lines="none">
<ion-avatar slot="start">
<img :src="user.picture" :alt="user.name" />
</ion-avatar>
<ion-label>
<ion-card-title>{{ user.name }}</ion-card-title>
<p>{{ user.email }}</p>
</ion-label>
</ion-item>
</ion-card-header>
<ion-card-content>
<pre>{{ JSON.stringify(user, null, 2) }}</pre>
</ion-card-content>
</ion-card>
</template>Protected Routes
Use Vue Router navigation guards with createAuthGuard to protect Ionic pages:
// src/router/index.ts
import { createRouter, createWebHistory } from '@ionic/vue-router';
import { createAuthGuard } from '@auth0/auth0-vue';
import type { App } from 'vue';
export function setupRouter(app: App) {
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
redirect: '/home'
},
{
path: '/home',
component: () => import('../views/HomePage.vue')
},
{
path: '/profile',
component: () => import('../views/ProfilePage.vue'),
beforeEnter: createAuthGuard(app)
}
]
});
return router;
}Alternative: Component-Level Guard
<script setup lang="ts">
import { watchEffect } from 'vue';
import { useAuth0 } from '@auth0/auth0-vue';
import { IonPage, IonContent, IonSpinner } from '@ionic/vue';
const { isAuthenticated, isLoading, loginWithRedirect } = useAuth0();
import { Browser } from '@capacitor/browser';
watchEffect(() => {
if (!isLoading.value && !isAuthenticated.value) {
loginWithRedirect({
async openUrl(url: string) {
await Browser.open({ url, windowName: "_self" });
}
});
}
});
</script>
<template>
<ion-page>
<ion-content v-if="isLoading" class="ion-text-center ion-padding">
<ion-spinner />
</ion-content>
<ion-content v-else-if="isAuthenticated">
<h1>Protected Content</h1>
</ion-content>
</ion-page>
</template>Accessing API Tokens
<script setup lang="ts">
import { ref } from 'vue';
import { useAuth0 } from '@auth0/auth0-vue';
const { getAccessTokenSilently } = useAuth0();
const data = ref(null);
const error = ref<string | null>(null);
const loading = ref(false);
const callApi = async () => {
loading.value = true;
error.value = null;
try {
const token = await getAccessTokenSilently({
authorizationParams: {
audience: "https://api.example.com/",
scope: "read:data",
}
});
const response = await fetch("https://api.example.com/data", {
headers: {
Authorization: `Bearer ${token}`,
},
});
data.value = await response.json();
} catch (err: any) {
error.value = err.message;
} finally {
loading.value = false;
}
};
</script>
<template>
<div>
<ion-button @click="callApi" :disabled="loading">
{{ loading ? 'Loading...' : 'Call API' }}
</ion-button>
<div v-if="error" class="error">{{ error }}</div>
<pre v-if="data">{{ JSON.stringify(data, null, 2) }}</pre>
</div>
</template>To use API tokens, configure the audience in the Auth0 plugin:
app.use(
createAuth0({
domain,
clientId,
useRefreshTokens: true,
useRefreshTokensFallback: false,
authorizationParams: {
redirect_uri: callbackUri,
audience: "https://api.example.com/",
}
})
);Conditional Login/Logout UI
<script setup lang="ts">
import { useAuth0 } from '@auth0/auth0-vue';
import { Browser } from '@capacitor/browser';
import { IonButton } from '@ionic/vue';
const { isAuthenticated, loginWithRedirect, logout } = useAuth0();
const domain = "your-tenant.auth0.com";
const packageId = "com.example.myapp";
const callbackUri = `${packageId}://${domain}/capacitor/${packageId}/callback`;
const login = async () => {
await loginWithRedirect({
async openUrl(url: string) {
await Browser.open({ url, windowName: "_self" });
}
});
};
const doLogout = async () => {
await logout({
logoutParams: { returnTo: callbackUri },
async openUrl(url: string) {
await Browser.open({ url, windowName: "_self" });
}
});
};
</script>
<template>
<ion-button v-if="isAuthenticated" @click="doLogout">Log out</ion-button>
<ion-button v-else @click="login">Log in</ion-button>
</template>Organizations Support
await loginWithRedirect({
authorizationParams: {
organization: "org_abc123",
},
async openUrl(url: string) {
await Browser.open({ url, windowName: "_self" });
}
});To accept an organization invitation:
await loginWithRedirect({
authorizationParams: {
organization: "org_abc123",
invitation: "inv_xyz789",
},
async openUrl(url: string) {
await Browser.open({ url, windowName: "_self" });
}
});Error Handling
<script setup lang="ts">
import { useAuth0 } from '@auth0/auth0-vue';
import { IonSpinner, IonCard, IonCardContent } from '@ionic/vue';
const { error, isLoading } = useAuth0();
</script>
<template>
<ion-spinner v-if="isLoading" />
<ion-card v-else-if="error" color="danger">
<ion-card-content>
<h2>Authentication Error</h2>
<p>{{ error.message }}</p>
</ion-card-content>
</ion-card>
<slot v-else />
</template>Common Error Types
| Error | Cause | Resolution |
|---|---|---|
login_required | Session expired or not authenticated | Re-trigger loginWithRedirect() |
consent_required | User hasn't consented to requested scopes | Re-trigger login with prompt: 'consent' |
invalid_grant | Refresh token expired or revoked | Clear session and re-authenticate |
access_denied | User denied consent or rule blocked access | Check Auth0 Actions/Rules for blocks |
mfa_required | MFA is required for the user | Handle MFA enrollment flow |
Testing Patterns
Physical Device Testing
Always test authentication flows on a physical device. Simulators and emulators may not correctly handle deep link callbacks or system browser interactions. To test on a physical device:
ionic build
npx cap sync
npx cap open ios # Build and run on device from Xcode
npx cap open android # Build and run on device from Android StudioManual Testing Flow
1. Run ionic serve for browser testing (limited — deep links won't work) 2. Build and deploy to a physical device:
ionic build
npx cap sync
npx cap open ios # or: npx cap open android3. Build and run from Xcode/Android Studio on a physical device 4. Tap Login → should open system browser 5. Authenticate → should return to app with user data 6. Tap Logout → should clear session and redirect back
Auth0 Ionic Vue (Capacitor) — Setup Guide
Auth0 Configuration
Agent instruction:
>
IMPORTANT — Never display credentials: After obtaining credentials from the CLI or user input, write them directly into config files. Do NOT echo, print, or display the domain, client ID, or any credential values in conversation output.
>
Always ask the user to choose between automatic and manual setup using AskUserQuestion:_"How would you like to configure Auth0 for this Ionic Vue project?"_
- Automatic setup (Recommended) — uses the Auth0 CLI to create a Native application, configure callback URLs, and store credentials in the project .env file automatically - Manual setup — you provide an existing .env file or Auth0 credentials (domain, client ID) and the agent writes them to the project config>
If credentials are already provided in the user's prompt: Use them directly — skip to "SDK Installation" below. Do NOT call AskUserQuestion to re-confirm.Auth0 CLI Automated Setup
Agent instruction: Execute these steps in order. Do NOT runauth0 loginfrom the agent — it is interactive and will hang. All otherauth0CLI commands below are non-interactive and safe to run.
>
---
>
#### Step A — Check Auth0 CLI is installed
>
```bash
command -v auth0
```
>
If missing, install it:
```bash
brew install auth0/auth0-cli/auth0
```
On Linux: curl -sSfL https://raw.githubusercontent.com/auth0/auth0-cli/main/install.sh | sh>
---
>
#### Step B — Verify Auth0 CLI login session
>
```bash
auth0 tenants list --csv --no-input 2>&1
```
>
- If the command succeeds and returns CSV output with tenant rows: proceed to Step C.
- If the command fails (exit code non-zero, or output contains "login" / "unauthorized" / is empty):
- Tell the user: _"You're not logged in to the Auth0 CLI. Please run auth0 login in your terminal and let me know when done."_ - Use AskUserQuestion to wait for confirmation.- After confirmation, re-run this check. Retry up to 3 times.
- If still failing after 3 retries: use AskUserQuestion to ask the user for their Auth0 Domain and Client ID manually, then skip to Step F.>
---
>
#### Step C — Detect active Auth0 tenant domain
>
Parse the CSV output from Step B. The active tenant line contains → (Unicode arrow U+2192).>
```
Example output:
ACTIVE,DOMAIN
→,dev-example.us.auth0.com
,dev-other.us.auth0.com
```
>
Extract the domain from the second column of the→line (e.g.,dev-example.us.auth0.com).
>
Tell the user: _"Your active Auth0 tenant is: <domain>. Is this correct?"_- If no, ask the user to run auth0 tenants use <correct-tenant-domain>, then re-run Step B.>
Store this as AUTH0_DOMAIN.>
---
>
#### Step D — Detect package ID from Capacitor config
>
Readcapacitor.config.ts(orcapacitor.config.json) in the project root:
>
- For.ts: parseappId: 'com.example.myapp'using regex.
- For.json: parse theappIdfield from JSON.
>
Store this asPACKAGE_ID(e.g.,com.example.myapp).
>
Also extractappNameif available (for the Auth0 app display name). Fall back to the project name frompackage.jsonif not found.
>
---
>
#### Step E — Create Native Auth0 application
>
Build the callback URL: PACKAGE_ID://AUTH0_DOMAIN/capacitor/PACKAGE_ID/callback>
```bash
auth0 apps create \
--name "APP_NAME" \
--type native \
--auth-method none \
--callbacks "PACKAGE_ID://AUTH0_DOMAIN/capacitor/PACKAGE_ID/callback" \
--logout-urls "PACKAGE_ID://AUTH0_DOMAIN/capacitor/PACKAGE_ID/callback" \
--origins "capacitor://localhost,http://localhost" \
--json \
--no-input
```
>
ReplaceAPP_NAME,PACKAGE_ID, andAUTH0_DOMAINwith the actual values from Steps C and D.
>
Parse the JSON output to extract client_id. Example response:```json
{
"client_id": "abc123def456...",
"name": "my-app",
"app_type": "native",
...
}
```
>
Storeclient_idasAUTH0_CLIENT_ID.
>
If this command fails due to session expiry, ask the user to run auth0 login again and retry. Retry up to 3 times.>
---
>
#### Step F — Write .env with real credentials>
Write (or update) the .env file in the project root with the actual values from Steps C–E:>
```bash
VITE_AUTH0_DOMAIN=AUTH0_DOMAIN
VITE_AUTH0_CLIENT_ID=AUTH0_CLIENT_ID
VITE_AUTH0_CALLBACK_URI=PACKAGE_ID://AUTH0_DOMAIN/capacitor/PACKAGE_ID/callback
```
>
ReplaceAUTH0_DOMAIN,AUTH0_CLIENT_ID, andPACKAGE_IDwith the actual values.
>
- If `.env` already exists: Update or add these three variables without removing other existing variables.
- If `.env` does not exist: Create the file.
- If `.gitignore` does not include `.env`: Add.envto.gitignore.
>
---
>
#### Step G — Update src/main.ts to use env vars>
Readsrc/main.tsand wire it to read credentials fromimport.meta.env:
>
If `createAuth0()` already exists in the file:
- Replace any hardcodeddomainvalue (e.g.,"YOUR_AUTH0_DOMAIN"or a real domain string) withimport.meta.env.VITE_AUTH0_DOMAIN.
- Replace any hardcodedclientIdvalue withimport.meta.env.VITE_AUTH0_CLIENT_ID.
- Replace theredirect_urivalue with `${packageId}://${import.meta.env.VITE_AUTH0_DOMAIN}/capacitor/${packageId}/callback(wherepackageId` is read from the Capacitor config or hardcoded if it never changes).
>
If `createAuth0()` does NOT exist in the file:
1. Add the import: import { createAuth0 } from '@auth0/auth0-vue';2. Add the Auth0 plugin registration beforerouter.isReady()orapp.mount():
```typescript
const packageId = "PACKAGE_ID"; // From capacitor.config.ts appId
>
app.use(
createAuth0({
domain: import.meta.env.VITE_AUTH0_DOMAIN,
clientId: import.meta.env.VITE_AUTH0_CLIENT_ID,
useRefreshTokens: true,
useRefreshTokensFallback: false,
authorizationParams: {
redirect_uri: ${packageId}://${import.meta.env.VITE_AUTH0_DOMAIN}/capacitor/${packageId}/callback}
})
);
```
>
Replace PACKAGE_ID with the actual package ID from Step D.>
---
>
#### Step H — Confirm setup to user (never display credentials)
>
After completing all steps, tell the user:
- _"Auth0 application created and configured successfully."_
- _"Credentials have been written to.env(VITE_AUTH0_DOMAINandVITE_AUTH0_CLIENT_ID)."_
- _"src/main.tsreads credentials fromimport.meta.env."_
>
Do NOT display the actual domain, client ID, or callback URL values. Only confirm that the setup succeeded and where the credentials were saved.
>
If the CLI keeps failing after retries, fall back to Manual Setup below.
Manual Setup (User-Provided Configuration)
Agent instruction: Ask the user to provide their Auth0 configuration. Accept either:
- An `.env` file path — read the file to extract the Auth0 domain and client ID, then copy or reference it in the project.
- Direct credentials — ask using AskUserQuestion: _"Please provide your Auth0 Domain and Client ID."_>
Once credentials are obtained, write them to the project.envfile usingVITE_AUTH0_DOMAINandVITE_AUTH0_CLIENT_IDvariable names. Do NOT display the credentials in conversation output.
Callback URL Format
| Field | Value |
|---|---|
| Allowed Callback URLs | YOUR_PACKAGE_ID://YOUR_DOMAIN/capacitor/YOUR_PACKAGE_ID/callback |
| Allowed Logout URLs | YOUR_PACKAGE_ID://YOUR_DOMAIN/capacitor/YOUR_PACKAGE_ID/callback |
| Allowed Web Origins | capacitor://localhost, http://localhost |
Replace YOUR_PACKAGE_ID with your app's package ID (e.g., com.example.myapp) and YOUR_DOMAIN with your Auth0 domain. These are set automatically when using the CLI commands above.
SDK Installation
npm install @auth0/auth0-vue @capacitor/browser @capacitor/app
npx cap syncPlugin purposes
| Package | Purpose |
|---|---|
@auth0/auth0-vue | Auth0 Vue SDK — provides createAuth0 plugin and useAuth0 composable |
@capacitor/browser | Opens Auth0 Universal Login in system browser (SFSafariViewController / Chrome Custom Tabs) |
@capacitor/app | Handles deep link callbacks from Auth0 after login/logout |
Post-Setup Steps
1. Verify Capacitor Configuration
Ensure capacitor.config.ts has the correct appId:
import type { CapacitorConfig } from '@capacitor/cli';
const config: CapacitorConfig = {
appId: 'com.example.myapp', // Must match YOUR_PACKAGE_ID in callback URLs
appName: 'My App',
webDir: 'dist',
server: {
androidScheme: 'https'
}
};
export default config;2. Sync Native Projects
After installing plugins, always sync:
npx cap sync3. Verify Platform Setup
iOS: Open the iOS project to verify:
npx cap open iosEnsure the Bundle Identifier in Xcode matches appId in capacitor.config.ts.
Android: Open the Android project to verify:
npx cap open androidEnsure applicationId in android/app/build.gradle matches appId in capacitor.config.ts.
Secret Management
Ionic Vue + Capacitor apps are Native applications — they do not use a client secret.
- Configuration contains only: Domain, Client ID, and Callback URL
- These values are not secrets and can be included in source code
- Token validation uses PKCE (Proof Key for Code Exchange) — no client secret needed
- Never include a client secret in a mobile/native application
Environment Variables (Optional)
If you prefer environment variables for Domain and Client ID during development:
# .env (for Vite-based Ionic Vue projects)
VITE_AUTH0_DOMAIN=your-tenant.auth0.com
VITE_AUTH0_CLIENT_ID=your-client-idThen reference in code:
app.use(
createAuth0({
domain: import.meta.env.VITE_AUTH0_DOMAIN,
clientId: import.meta.env.VITE_AUTH0_CLIENT_ID,
useRefreshTokens: true,
useRefreshTokensFallback: false,
authorizationParams: {
redirect_uri: `${packageId}://${import.meta.env.VITE_AUTH0_DOMAIN}/capacitor/${packageId}/callback`
}
})
);Verification
After setup, verify the configuration:
1. Run ionic serve — the app should load without Auth0 errors 2. Run ionic build && npx cap sync — native projects should sync cleanly 3. Open in Xcode/Android Studio and build — no missing plugin errors 4. Tap login — system browser should open Auth0 Universal Login 5. After login — app should receive the deep link callback and show the user profile
{
"metadata": {
"skill_name": "auth0-ionic-vue",
"sdk_type": "NATIVE_MOBILE",
"framework": "Ionic Vue (Capacitor)",
"language": "TypeScript",
"package": "@auth0/auth0-vue",
"generated_by": "quickstart-skill-generator",
"generated_date": "2026-04-24T00:00:00.000Z"
},
"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": "NATIVE_MOBILE",
"framework": "Ionic Vue (Capacitor)",
"language": "TypeScript"
},
"graders": {
"source": "graders.json",
"total_count": 22,
"breakdown": {
"contains_matches": 16,
"file_contains": 2,
"not_contains": 2,
"judge": 2
}
},
"scoring": {
"dimensions": {
"correctness": {"weight": 0.25, "source": "contains/matches graders"},
"credential_placement": {"weight": 0.15, "source": "file_contains graders"},
"hallucination": {"weight": 0.15, "source": "not_contains graders"},
"security": {"weight": 0.15, "source": "security not_contains graders"},
"semantic": {"weight": 0.30, "source": "judge graders"}
}
},
"thresholds": {
"skill_valuable": "delta.without_skill_to_with_skill >= 0.25",
"skill_acceptable": "with_skill.pass_rate >= 0.85",
"needs_improvement": "delta.without_skill_to_with_skill < 0.15"
},
"feedback": {
"generate": true,
"output": "feedback.json"
}
}
{
"skill_name": "auth0-ionic-vue",
"evals": [
{
"id": 1,
"prompt": "Add Auth0 authentication to an Ionic Vue application with Capacitor using the @auth0/auth0-vue SDK.\n\n**Auth0 Credentials:**\n- Domain: `dev-example.auth0.com`\n- Client ID: `abc123def456ghi789jkl012`",
"expected_output": "Working Ionic Vue + Capacitor app with Auth0 login via Capacitor Browser, deep link callback handling, logout, and user profile display",
"expectations": [
"Auth0 Vue SDK installed via any valid method (npm or yarn)",
"Has correct import statement for @auth0/auth0-vue",
"Auth0 SDK initialized (createAuth0 plugin registered or useAuth0 composable used)",
"Implements login functionality via loginWithRedirect",
"Implements logout functionality",
"Uses Capacitor Browser plugin for native browser opening",
"Uses Capacitor App plugin for deep link handling",
"Uses secure credential storage via refresh tokens (useRefreshTokens)",
"Auth0 domain dev-example.auth0.com written to config or source file",
"Client ID abc123def456ghi789jkl012 written to config or source file",
"No client_secret in native app",
"Does not use window.location.origin as redirect URI (must use custom scheme)",
"Correctly integrates Auth0 into Ionic Vue + Capacitor app with login, logout, deep link callback, and custom URL scheme",
"Uses current SDK version (2.4.0 or newer) in dependency declaration",
"Enables refresh tokens (useRefreshTokens: true) required for Capacitor",
"Disables refresh token iframe fallback (useRefreshTokensFallback: false) required for Capacitor",
"Calls handleRedirectCallback to process deep link auth response",
"Closes Capacitor Browser after handling callback",
"Listens for appUrlOpen deep link event from Capacitor App plugin",
"Uses Capacitor-specific advanced patterns: refresh token config, Browser.open in openUrl, appUrlOpen listener with handleRedirectCallback, Browser.close",
"Uses Vue-specific patterns: createAuth0 plugin, useAuth0 composable, script setup or setup function",
"Registers Auth0 plugin with app.use(createAuth0(...)) before mounting"
]
}
]
}
[
{"type": "matches", "pattern": "npm install @auth0/auth0-vue|yarn add @auth0/auth0-vue|\"@auth0/auth0-vue\"\\s*:", "description": "Auth0 Vue SDK installed via any valid method"},
{"type": "matches", "pattern": "import\\s+.*from\\s+['\"]@auth0/auth0-vue['\"]|require\\(['\"]@auth0/auth0-vue['\"]\\)", "description": "Has correct import statement for @auth0/auth0-vue"},
{"type": "contains_any", "values": ["createAuth0", "useAuth0"], "description": "Auth0 SDK initialized (plugin registered or composable used)"},
{"type": "contains", "value": "loginWithRedirect", "description": "Implements login functionality via loginWithRedirect"},
{"type": "matches", "pattern": "logout\\s*\\(", "description": "Implements logout functionality"},
{"type": "matches", "pattern": "@capacitor/browser|Browser\\.open\\s*\\(", "description": "Uses Capacitor Browser plugin for native browser opening"},
{"type": "matches", "pattern": "@capacitor/app|CapApp\\.addListener|App\\.addListener", "description": "Uses Capacitor App plugin for deep link handling"},
{"type": "contains", "value": "useRefreshTokens", "description": "Uses secure credential storage via refresh tokens (useRefreshTokens)"},
{"type": "file_contains", "file_pattern": "**/*.{vue,ts,js,env*}", "value": "dev-example.auth0.com", "description": "Auth0 domain written to config or source file"},
{"type": "file_contains", "file_pattern": "**/*.{vue,ts,js,env*}", "value": "abc123def456ghi789jkl012", "description": "Client ID written to config or source file"},
{"type": "not_contains", "value": "client_secret", "description": "No client secret in native app (not needed)"},
{"type": "not_contains", "value": "window.location.origin", "description": "Does not use window.location.origin as redirect URI (must use custom scheme)"},
{"type": "judge", "id": "correctness", "description": "Does the solution correctly integrate Auth0 with login, logout, deep links, and custom URL scheme", "question": "Does the solution correctly integrate Auth0 into an Ionic Vue + Capacitor native mobile app with working login via Capacitor Browser, deep link callback handling via Capacitor App plugin, logout, and proper custom URL scheme configuration? It should use Vue-specific APIs (createAuth0, useAuth0 composable) rather than React APIs.", "examples": "PASS: Uses createAuth0 plugin with loginWithRedirect using Browser.open in openUrl callback, handles appUrlOpen deep link events with handleRedirectCallback, implements logout with Browser.open, and uses custom scheme callback URL.\nFAIL: Only implements basic Vue SPA login without Capacitor Browser/App plugins.\nFAIL: Uses window.location.origin as redirect URI instead of custom URL scheme.\nFAIL: Uses React SDK (Auth0Provider, useAuth0 from @auth0/auth0-react) instead of Vue SDK.", "framework": "ionic-vue"},
{"type": "matches", "pattern": "2\\.[4-9]\\.|2\\.\\d\\d\\.|[3-9]\\.\\d", "description": "Uses current SDK version (2.4.0 or newer) in dependency declaration"},
{"type": "matches", "pattern": "useRefreshTokens\\s*:\\s*true", "description": "Enables refresh tokens (required for Capacitor - no iframe available)"},
{"type": "matches", "pattern": "useRefreshTokensFallback\\s*:\\s*false", "description": "Disables refresh token iframe fallback (required for Capacitor)"},
{"type": "matches", "pattern": "handleRedirectCallback\\s*\\(", "description": "Calls handleRedirectCallback to process deep link auth response"},
{"type": "matches", "pattern": "Browser\\.close\\s*\\(", "description": "Closes Capacitor Browser after handling callback"},
{"type": "matches", "pattern": "appUrlOpen", "description": "Listens for appUrlOpen deep link event from Capacitor App plugin"},
{"type": "judge", "id": "skill-attribution", "description": "Uses skill-specific advanced pattern knowledge for Capacitor + Vue integration", "question": "Check ONLY for these skill-specific advanced patterns (NOT basic SDK usage like createAuth0, loginWithRedirect, useAuth0). Look for at least 2 of: (1) useRefreshTokens: true AND useRefreshTokensFallback: false configured together in createAuth0, (2) Browser.open() used inside an openUrl callback parameter of loginWithRedirect, (3) CapApp.addListener('appUrlOpen', ...) with handleRedirectCallback inside the listener, (4) Browser.close() called after handleRedirectCallback. These are Capacitor-specific patterns taught by the skill that are NOT part of standard Vue SPA knowledge. Answer YES only if at least 2 of these advanced patterns are present, NO otherwise.", "examples": "PASS: Code has createAuth0 with useRefreshTokens: true and useRefreshTokensFallback: false AND uses Browser.open inside openUrl callback of loginWithRedirect.\nPASS: Code handles appUrlOpen deep link event calling handleRedirectCallback AND calls Browser.close() afterward.\nFAIL: Code only uses basic createAuth0 with loginWithRedirect() without any Capacitor-specific patterns.\nFAIL: Code has createAuth0 but uses window.location.origin as redirect_uri and no Capacitor plugins.", "framework": "ionic-vue"},
{"type": "matches", "pattern": "createAuth0\\s*\\(", "description": "Uses Vue-specific createAuth0 plugin factory (not React Auth0Provider)"},
{"type": "matches", "pattern": "app\\.use\\s*\\(\\s*createAuth0|app\\.use\\(\\s*\\n\\s*createAuth0", "description": "Registers Auth0 plugin with app.use(createAuth0(...)) before mounting"}
]
/**
* 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-vue-evals",
"version": "1.0.0",
"description": "Eval runner for auth0-ionic-vue 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 Vue 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 Vue application with Capacitor using the @auth0/auth0-vue SDK.
Auth0 Credentials:
- Domain:
dev-example.auth0.com - Client ID:
abc123def456ghi789jkl012