@elogroup-sereduc/ser-front-core-client
v4.0.6
Published
Complete OAuth/OIDC authentication system with Silent SSO, PKCE, and API client for Ser Educacional applications
Downloads
1,069
Readme
Ser Auth Library
Sistema de autenticação OAuth/OIDC compatível com Keycloak, desenvolvido como substituto para keycloak-js com suporte a Silent SSO, PKCE S256, e gerenciamento automático de tokens.
Características
- ✅ Silent SSO: Autenticação invisível via iframe
- ✅ PKCE S256: Segurança para SPAs (Single Page Applications)
- ✅ Token Refresh: Renovação automática de tokens
- ✅ API Client: Cliente HTTP com interceptadores automáticos
- ✅ Route Guards: Proteção de rotas para frameworks como TanStack Router
- ✅ Zustand Store: Gerenciamento de estado em memória
- ✅ IDP Hint: Suporte a redirecionamento automático para provedores específicos
- ✅ Impersonificação: Autenticação via código de autorização de curta duração
Instalação
npm install @elogroup-sereduc/ser-front-core-clientSetup Básico
1. Configuração Inicial
import {
createKeycloakAuthFromUrl,
initAuth,
} from "@elogroup-sereduc/ser-front-core-client";
// Criar instância do SerOAuth
const kc = createKeycloakAuthFromUrl(
"https://seu-keycloak.com/realms/seu-realm/protocol/openid-connect/auth",
"seu-client-id",
"seu-idp-hint", // opcional
);
// Inicializar autenticação
const authenticated = await initAuth(kc, "/seu-base-path");2. Arquivo Silent-Check-SSO
Copie o arquivo public/silent-check-sso.html para a pasta public da sua aplicação.
3. Context de Autenticação (React)
import { useAuthStore } from '@elogroup-sereduc/ser-front-core-client'
export function AuthContext({ children }) {
const authState = useAuthStore()
return (
<AuthProvider value={{
authenticated: authState.authenticated,
user: extractUserFromToken(authState.idToken),
login: () => kc.login(),
logout: () => kc.logout(),
}}>
{children}
</AuthProvider>
)
}4. Cliente API
import { createApiClient } from "@elogroup-sereduc/ser-front-core-client";
const api = createApiClient(kc, "https://sua-api.com");
// O cliente automaticamente:
// - Adiciona Bearer token nos headers
// - Renova token em caso de 401
// - Retenta requisição com novo token5. Route Guards
import { createAuthGuard } from "@elogroup-sereduc/ser-front-core-client";
import { createFileRoute } from "@tanstack/react-router";
const requireAuth = createAuthGuard(kc);
export const Route = createFileRoute("/protected")({
beforeLoad: requireAuth,
component: ProtectedComponent,
});API Reference
SerOAuth
class SerOAuth {
constructor(config: SerOAuthConfig);
// Métodos principais
async init(options?: InitOptions): Promise<boolean>;
async login(options?: LoginOptions): Promise<void>;
async logout(options?: LogoutOptions): Promise<void>;
async updateToken(minValidity?: number): Promise<boolean>;
async impersonation(
authorizationCode: string,
username: string,
): Promise<boolean>;
// Propriedades
get authenticated(): boolean;
get token(): string | null;
get refreshToken(): string | null;
get idToken(): string | null;
// Callbacks
onAuthSuccess?: () => void;
onAuthError?: (err: unknown) => void;
onAuthLogout?: () => void;
onTokenExpired?: () => void;
onTokenRefreshed?: () => void;
}Configuração
type SerOAuthConfig = {
url: string; // https://seu-keycloak.com
realm: string; // nome-do-realm
clientId: string; // seu-client-id
idp?: string; // hint para IDP específico
enableSsoLogout?: boolean; // se true, logout redireciona ao end_session_endpoint do IDP
authProviderUrl?: string; // URL base do auth provider interno (impersonificação)
internalRealm?: string; // realm usado nas rotas internas (/v1/{realm}/...)
};
type InitOptions = {
onLoad?: "check-sso" | "login-required";
silentCheckSsoRedirectUri?: string;
pkceMethod?: "S256";
};
type AuthorizeUserBody = {
authorizationCode: string;
username: string;
realm: string;
};Store (Zustand)
const authState = useAuthStore();
// {
// accessToken: string | null
// refreshToken: string | null
// idToken: string | null
// accessExpiresAt: number | null
// authenticated: boolean
// authType: "default" | "imperson"
// authProviderUrl?: string
// realm?: string
// setTokens: (tokens: TokenSet) => void
// setAuthType: (type: "default" | "imperson") => void
// clear: () => void
// }Migração do keycloak-js
Antes (keycloak-js)
import Keycloak from "keycloak-js";
const keycloak = new Keycloak({
url: "https://keycloak.com",
realm: "myrealm",
clientId: "myclient",
});
await keycloak.init({ onLoad: "check-sso" });Depois (ser-auth)
import {
createSerOAuth,
initAuth,
} from "@elogroup-sereduc/ser-front-core-client";
const kc = createSerOAuth({
url: "https://keycloak.com",
realm: "myrealm",
clientId: "myclient",
});
await initAuth(kc);Exemplos Avançados
Impersonificação
Fluxo para autenticar um usuário com um código de autorização de curta duração (sem OIDC). Requer authProviderUrl e internalRealm (ou authProviderUrl + realm via initAuth).
import {
createSerOAuth,
initAuth,
} from "@elogroup-sereduc/ser-front-core-client";
const kc = createSerOAuth({
url: "https://seu-keycloak.com",
realm: "seu-realm",
clientId: "seu-client-id",
authProviderUrl: "https://auth-provider.seu-dominio.com",
internalRealm: "seu-realm-interno",
});
await initAuth({
authClient: kc,
basePath: "/seu-base-path",
authProviderUrl: "https://auth-provider.seu-dominio.com",
realm: "seu-realm-interno",
});
// Troca o código pelo token (POST /v1/{realm}/imperson/authorize-user)
const ok = await kc.impersonation("codigo-de-autorizacao", "12345678");Request enviado pela biblioteca (POST, sem autenticação — o código é o segredo):
type AuthorizeUserBody = {
authorizationCode: string;
username: string;
realm: string;
};{
"authorizationCode": "…",
"username": "12345678",
"realm": "seu-realm-interno"
}O realm do body é o configurado via internalRealm / initAuth({ realm }).
Resposta esperada (200):
{
"accessToken": "…",
"refreshToken": "…",
"expiresIn": 300,
"refreshExpiresIn": 1800,
"tokenType": "Bearer",
"notBeforePolicy": 0,
"sessionState": "…",
"scope": "…"
}Comportamentos relevantes:
authTypepassa a"imperson"e o token é persistido emlocalStoragepara restaurar a sessão no próximoinit.- Tokens de impersonificação não suportam refresh; ao expirar, a sessão é encerrada automaticamente.
- No
logout, os dados de impersonificação são removidos dolocalStorage.
Custom Error Handling
kc.onAuthError = (error) => {
console.error("Auth error:", error);
// Redirecionar para página de erro
};
kc.onTokenExpired = () => {
console.log("Token expirado, renovando...");
};Múltiplas Instâncias
const adminKc = createSerOAuth({
url: "https://admin-sso.com",
realm: "admin",
clientId: "admin-client",
});
const userKc = createSerOAuth({
url: "https://user-sso.com",
realm: "users",
clientId: "user-client",
});Interceptação Customizada
const api = createApiClient(kc);
api.interceptors.request.use((config) => {
// Adicionar headers customizados
config.headers["X-Custom-Header"] = "value";
return config;
});