From affolternet-web-bff
Configure SPA integration for affolterNET.Web.Bff. Use when setting up Vue/React/Angular apps, handling 401 responses, static files, or SPA fallback routing.
How this skill is triggered — by the user, by Claude, or both
Slash command
/affolternet-web-bff:spa-integrationThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Configure single-page application integration with the BFF.
Configure single-page application integration with the BFF.
For complete reference, see Library Guide.
The BFF provides SPA-friendly authentication:
1. SPA makes API request
2. BFF returns 401 if not authenticated
3. SPA redirects to /bff/account/login
4. User authenticates with Keycloak
5. BFF creates session cookie
6. User redirected back to SPA
// api.ts - Axios interceptor
import axios from 'axios';
const api = axios.create({
baseURL: '/',
withCredentials: true
});
api.interceptors.response.use(
response => response,
error => {
if (error.response?.status === 401) {
const returnUrl = encodeURIComponent(window.location.pathname);
window.location.href = `/bff/account/login?returnUrl=${returnUrl}`;
}
return Promise.reject(error);
}
);
export default api;
Static files are served from wwwroot/:
wwwroot/
├── index.html
├── assets/
│ ├── main.js
│ └── style.css
└── favicon.ico
The BFF automatically handles SPA routing:
/api/* routes return 404 JSON if not foundindex.htmlexport default defineConfig({
build: {
outDir: '../wwwroot'
},
server: {
proxy: {
'/api': 'https://localhost:5001',
'/bff': 'https://localhost:5001'
}
}
});
import { defineStore } from 'pinia';
import api from '@/services/api';
export const useAuthStore = defineStore('auth', {
state: () => ({
user: null,
loading: false
}),
actions: {
async fetchUser() {
this.loading = true;
try {
const { data } = await api.get('/bff/account/user');
this.user = data;
} catch {
this.user = null;
} finally {
this.loading = false;
}
},
login(returnUrl?: string) {
const url = returnUrl
? `/bff/account/login?returnUrl=${encodeURIComponent(returnUrl)}`
: '/bff/account/login';
window.location.href = url;
},
async logout() {
window.location.href = '/bff/account/logout';
}
}
});
Include CSRF token in state-changing requests:
// Get token from cookie
function getCsrfToken(): string | null {
const match = document.cookie.match(/XSRF-TOKEN=([^;]+)/);
return match ? decodeURIComponent(match[1]) : null;
}
// Include in requests
api.interceptors.request.use(config => {
if (['post', 'put', 'delete', 'patch'].includes(config.method?.toLowerCase() ?? '')) {
config.headers['X-XSRF-TOKEN'] = getCsrfToken();
}
return config;
});
npx claudepluginhub affolternet/affolternet.web --plugin affolternet-web-bffAdds Auth0 authentication to framework-agnostic SPAs (Vanilla JS, Svelte, SolidJS) using @auth0/auth0-spa-js. Handles SDK setup, configuration, and login/logout flows.
Integrates OAuth2/OpenID Connect authentication into Vue 3 apps using @volverjs/auth-vue. Handles login/logout, authorization-code + PKCE flow, token refresh, route guards, and reactive auth state.
Adding auth to Blazor. AuthorizeView, CascadingAuthenticationState, Identity UI, per-model flows.