add-auth-provider
Centralizes auth provider logic for login, registration, and user session management.
Install
mkdir -p .claude/skills/add-auth-provider && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/13071" && unzip -o skill.zip -d .claude/skills/add-auth-provider && rm skill.zipInstalls to .claude/skills/add-auth-provider
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Use this skill when building or modifying the auth provider, login page, register page, or anything related to authentication state — token storage, login/logout actions, or reading the current user from context.Key capabilities
- →Manage login, register, and logout processes
- →Handle current user's session
- →Call real backend auth/session endpoints
- →Keep API calls within provider files
- →Define authentication state interfaces and initial state
- →Create Redux actions for authentication states
How it works
The skill manages authentication by defining interfaces, creating Redux actions and reducers, and handling API calls to backend endpoints.
Inputs & outputs
When to use add-auth-provider
- →Build login page
- →Update auth state management
- →Add user registration flow
About this skill
Mosaic-Talent — Auth Provider
Overview
The auth provider manages login, register, logout, and the current user's session. It differs from other providers because ABP's login endpoint (/api/TokenAuth/Authenticate) has a different shape than the standard CRUD services.
Production Auth Rules
- Never implement mock/demo auth logic (no simulated success delays, no fake auth shortcuts).
- Never persist auth session state in
localStorage. - Always call real backend auth/session endpoints via provider actions.
- Keep API calls in provider files only; pages/components must consume hooks/actions.
File Structure
providers/auth-provider/
├── context.tsx ← interfaces + initial state + createContext
├── actions.tsx ← redux-actions createAction calls
├── reducer.tsx ← handleActions reducer
└── index.tsx ← AuthProvider component + useAuthState + useAuthAction hooks
context.tsx
"use client";
import { createContext } from "react";
export interface IUser {
userId: number; // int64 from ABP — number not string
accessToken: string;
expireInSeconds: number;
}
export interface IAuthStateContext {
isPending: boolean;
isSuccess: boolean;
isError: boolean;
isAuthenticated: boolean;
user?: IUser;
}
export interface IAuthActionContext {
login: (userNameOrEmailAddress: string, password: string) => void;
register: (input: IRegisterInput) => void;
logout: () => void;
}
export interface IRegisterInput {
name: string;
surname: string;
userName: string;
emailAddress: string;
password: string;
}
export const INITIAL_STATE: IAuthStateContext = {
isPending: false,
isSuccess: false,
isError: false,
isAuthenticated: false,
};
export const AuthStateContext = createContext<IAuthStateContext>(INITIAL_STATE);
export const AuthActionContext = createContext<IAuthActionContext>({
login: () => {},
register: () => {},
logout: () => {},
});
actions.tsx
import { createAction } from "redux-actions";
import { IAuthStateContext, IUser } from "./context";
export enum AuthStateEnums {
LOGIN_PENDING = "LOGIN_PENDING",
LOGIN_SUCCESS = "LOGIN_SUCCESS",
LOGIN_ERROR = "LOGIN_ERROR",
REGISTER_PENDING = "REGISTER_PENDING",
REGISTER_SUCCESS = "REGISTER_SUCCESS",
REGISTER_ERROR = "REGISTER_ERROR",
LOGOUT_PENDING = "LOGOUT_PENDING",
LOGOUT_SUCCESS = "LOGOUT_SUCCESS",
LOGOUT_ERROR = "LOGOUT_ERROR",
}
export const loginPending = createAction<IAuthStateContext>(
AuthStateEnums.LOGIN_PENDING,
() => ({
isPending: true,
isSuccess: false,
isError: false,
isAuthenticated: false,
}),
);
export const loginSuccess = createAction<IAuthStateContext, IUser>(
AuthStateEnums.LOGIN_SUCCESS,
(user: IUser) => ({
isPending: false,
isSuccess: true,
isError: false,
isAuthenticated: true,
user,
}),
);
export const loginError = createAction<IAuthStateContext>(
AuthStateEnums.LOGIN_ERROR,
() => ({
isPending: false,
isSuccess: false,
isError: true,
isAuthenticated: false,
}),
);
export const registerPending = createAction<IAuthStateContext>(
AuthStateEnums.REGISTER_PENDING,
() => ({
isPending: true,
isSuccess: false,
isError: false,
isAuthenticated: false,
}),
);
export const registerSuccess = createAction<IAuthStateContext, IUser>(
AuthStateEnums.REGISTER_SUCCESS,
(user: IUser) => ({
isPending: false,
isSuccess: true,
isError: false,
isAuthenticated: true,
user,
}),
);
export const registerError = createAction<IAuthStateContext>(
AuthStateEnums.REGISTER_ERROR,
() => ({
isPending: false,
isSuccess: false,
isError: true,
isAuthenticated: false,
}),
);
export const logoutPending = createAction<IAuthStateContext>(
AuthStateEnums.LOGOUT_PENDING,
() => ({
isPending: true,
isSuccess: false,
isError: false,
isAuthenticated: false,
}),
);
export const logoutSuccess = createAction<IAuthStateContext>(
AuthStateEnums.LOGOUT_SUCCESS,
() => ({
isPending: false,
isSuccess: true,
isError: false,
isAuthenticated: false,
user: undefined,
}),
);
export const logoutError = createAction<IAuthStateContext>(
AuthStateEnums.LOGOUT_ERROR,
() => ({
isPending: false,
isSuccess: false,
isError: true,
isAuthenticated: false,
}),
);
reducer.tsx
import { handleActions } from "redux-actions";
import { INITIAL_STATE, IAuthStateContext } from "./context";
import { AuthStateEnums } from "./actions";
export const AuthReducer = handleActions<IAuthStateContext, IAuthStateContext>(
{
[AuthStateEnums.LOGIN_PENDING]: (state, { payload }) => ({
...state,
...payload,
}),
[AuthStateEnums.LOGIN_SUCCESS]: (state, { payload }) => ({
...state,
...payload,
}),
[AuthStateEnums.LOGIN_ERROR]: (state, { payload }) => ({
...state,
...payload,
}),
[AuthStateEnums.REGISTER_PENDING]: (state, { payload }) => ({
...state,
...payload,
}),
[AuthStateEnums.REGISTER_SUCCESS]: (state, { payload }) => ({
...state,
...payload,
}),
[AuthStateEnums.REGISTER_ERROR]: (state, { payload }) => ({
...state,
...payload,
}),
[AuthStateEnums.LOGOUT_PENDING]: (state, { payload }) => ({
...state,
...payload,
}),
[AuthStateEnums.LOGOUT_SUCCESS]: (state, { payload }) => ({
...state,
...payload,
}),
[AuthStateEnums.LOGOUT_ERROR]: (state, { payload }) => ({
...state,
...payload,
}),
},
INITIAL_STATE,
);
index.tsx
"use client";
import { useContext, useReducer } from "react";
import { getAxiosInstance, setAuthToken, removeAuthToken } from "@/utils/axiosInstance";
import { AuthReducer } from "./reducer";
import { INITIAL_STATE, AuthStateContext, AuthActionContext, IRegisterInput } from "./context";
import {
loginPending, loginSuccess, loginError,
registerPending, registerSuccess, registerError,
logoutPending, logoutSuccess, logoutError,
} from "./actions";
export const AuthProvider = ({ children }: { children: React.ReactNode }) => {
const instance = getAxiosInstance();
const [state, dispatch] = useReducer(AuthReducer, INITIAL_STATE);
const login = async (userNameOrEmailAddress: string, password: string) => {
dispatch(loginPending());
try {
const res = await instance.post('/api/TokenAuth/Authenticate', {
userNameOrEmailAddress, // ← ABP field name, not "email"
password,
rememberClient: true,
});
const { accessToken, expireInSeconds, userId } = res.data.result;
setAuthToken(accessToken); // persist token to cookie
dispatch(loginSuccess({ accessToken, expireInSeconds, userId }));
} catch {
dispatch(loginError());
}
};
const register = async (input: IRegisterInput) => {
dispatch(registerPending());
try {
const res = await instance.post('/api/services/app/Account/Register', input);
// After register, auto-login
await login(input.userName, input.password);
} catch {
dispatch(registerError());
}
};
const logout = async () => {
dispatch(logoutPending());
try {
removeAuthToken();
dispatch(logoutSuccess());
} catch {
dispatch(logoutError());
}
};
return (
<AuthStateContext.Provider value={state}>
<AuthActionContext.Provider value={{ login, register, logout }}>
{children}
</AuthActionContext.Provider>
</AuthStateContext.Provider>
);
};
export const useAuthState = () => {
const context = useContext(AuthStateContext);
if (!context) throw new Error("useAuthState must be used within AuthProvider");
return context;
};
export const useAuthAction = () => {
const context = useContext(AuthActionContext);
if (!context) throw new Error("useAuthAction must be used within AuthProvider");
return context;
};
Usage in Components
// Reading auth state
const { isAuthenticated, isPending, isError, user } = useAuthState();
// Triggering auth actions
const { login, logout, register } = useAuthAction();
// Login
await login("admin", "Admin@123");
// Logout
logout();
// Check role
const isHRAdmin = user?.roleNames?.includes("HRAdmin");
Provider Composition (after creating or modifying AuthProvider)
After building or updating the auth provider, register it in the project's provider composition file. Follow these steps exactly.
Step 1 — Find the composition file
Look for an existing composition file in the providers directory. Common names:
src/providers/index.tsxsrc/providers/AppProviders.tsxsrc/providers/Providers.tsx
Read it if it exists. If it does not exist, create it at src/providers/index.tsx.
Step 2 — Position AuthProvider as the outermost wrapper
AuthProvider must always wrap all other providers. Per the nesting order rule:
AuthProvider ← outermost (Auth / Session)
ThemeProvider ← Theme / Style
QueryProvider ← Data / Query
OtherProviders ← Domain feature providers
{children}
If the composition file already exists and AuthProvider is not the outermost wrapper, move it to the outside. Preserve all other providers' order.
Step 3 — Generate or update the composition file
The file must follow this shape (adapt 'use client' and import style to match existing files):
"use client";
import { AuthProvider } from "./auth-provider";
// … one import per other provider …
interface AppProvidersProps {
children: React.ReactNode;
}
export const AppProviders: React.FC<AppProvidersProps> = ({ children }) => {
return (
<AuthProvider>
{/* other providers nested here */}
{children}
</AuthProvider>
);
};
Rules:
- Use named export
AppProvidersunless the file already uses a different name — preserve the existi
Content truncated.
When not to use it
- →When implementing mock or demo authentication logic
- →When persisting authentication session state in localStorage
- →When pages or components directly make API calls for authentication
Limitations
- →Never implement mock/demo auth logic
- →Never persist auth session state in `localStorage`
- →Keep API calls in provider files only
How it compares
This workflow enforces strict rules for authentication management, such as avoiding local storage and centralizing API calls, which differs from less secure or less structured approaches.
Compared to similar skills
add-auth-provider side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| add-auth-provider (this skill) | 0 | 4mo | No flags | Intermediate |
| debugging-streamlit | 7 | 5mo | Review | Intermediate |
| fullstack-guardian | 1 | 3mo | No flags | Advanced |
| javascript-typescript-typescript-scaffold | 3 | 4mo | Review | Beginner |
Try saying
Example prompts that trigger this skill in your AI assistant.
You might also like
debugging-streamlit
streamlit
Debug Streamlit frontend and backend changes using make debug with hot-reload. Use when testing code changes, investigating bugs, checking UI behavior, or needing screenshots of the running app.
fullstack-guardian
Jeffallan
Use when implementing features across frontend and backend, building APIs with UI, or creating end-to-end data flows. Invoke for feature implementation, API development, UI building, cross-stack work.
javascript-typescript-typescript-scaffold
sickn33
You are a TypeScript project architecture expert specializing in scaffolding production-ready Node.js and frontend applications. Generate complete project structures with modern tooling (pnpm, Vite, N
podcast-generation
microsoft
Generate AI-powered podcast-style audio narratives using Azure OpenAI's GPT Realtime Mini model via WebSocket. Use when building text-to-speech features, audio narrative generation, podcast creation from content, or integrating with Azure OpenAI Realtime API for real audio output. Covers full-stack implementation from React frontend to Python FastAPI backend with WebSocket streaming.
convex-realtime
waynesutton
Patterns for building reactive apps including subscription management, optimistic updates, cache behavior, and paginated queries with cursor-based loading
raw-app
windmill-labs
MUST use when creating raw apps.