{"id":51225787,"url":"https://github.com/ricardoqmd/auth","last_synced_at":"2026-06-28T11:02:13.790Z","repository":{"id":356150892,"uuid":"1227550564","full_name":"ricardoqmd/auth","owner":"ricardoqmd","description":"Reusable authentication primitives for Next.js + Keycloak. XState-powered, framework-agnostic core.","archived":false,"fork":false,"pushed_at":"2026-06-23T05:55:06.000Z","size":256,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-23T07:24:23.050Z","etag":null,"topics":["authentication","keycloak","nextjs","oidc","rbac","react","sso","typescript","xstate"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ricardoqmd.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":"ROADMAP.md","authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-02T20:57:00.000Z","updated_at":"2026-06-23T05:55:08.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/ricardoqmd/auth","commit_stats":null,"previous_names":["ricardoqmd/auth"],"tags_count":17,"template":false,"template_full_name":null,"purl":"pkg:github/ricardoqmd/auth","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ricardoqmd%2Fauth","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ricardoqmd%2Fauth/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ricardoqmd%2Fauth/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ricardoqmd%2Fauth/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ricardoqmd","download_url":"https://codeload.github.com/ricardoqmd/auth/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ricardoqmd%2Fauth/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34885802,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-28T02:00:05.809Z","response_time":54,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["authentication","keycloak","nextjs","oidc","rbac","react","sso","typescript","xstate"],"created_at":"2026-06-28T11:02:13.170Z","updated_at":"2026-06-28T11:02:13.779Z","avatar_url":"https://github.com/ricardoqmd.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# @ricardoqmd/auth\n\n\u003e Reusable authentication primitives for Next.js apps using Keycloak — designed for SPAs, with a clean state machine at the core and pluggable adapters.\n\n[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ricardoqmd_auth\u0026metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ricardoqmd_auth)\n[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=ricardoqmd_auth\u0026metric=coverage)](https://sonarcloud.io/summary/new_code?id=ricardoqmd_auth)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE)\n[![pnpm](https://img.shields.io/badge/pnpm-9-orange)](https://pnpm.io/)\n[![TypeScript](https://img.shields.io/badge/TypeScript-5-blue)](https://www.typescriptlang.org/)\n\n## Why?\n\nSpinning up a new Next.js app with Keycloak shouldn't take a sprint. This monorepo packages the OIDC dance once — token init, refresh, logout, RBAC helpers — so every new project just runs `npm install` and gets a working `\u003cAuthProvider\u003e` and `useAuth()` hook with redirect-on-boot behavior out of the box.\n\n## Architecture\n\n```\n┌─────────────────────────────────────────────────────────────┐\n│                    Your Next.js app                         │\n│                                                             │\n│   \u003cAuthProvider\u003e     useAuth() { token, user, hasRole, ... }│\n└──────────────────────────┬──────────────────────────────────┘\n                           │\n                ┌──────────▼──────────┐\n                │ @ricardoqmd/        │\n                │   auth-nextjs       │   ← React/Next.js bindings\n                └──────────┬──────────┘\n                           │\n                ┌──────────▼──────────┐\n                │ @ricardoqmd/        │\n                │   auth-core         │   ← XState machine (framework-agnostic)\n                └──────────┬──────────┘\n                           │ AuthProvider interface\n                ┌──────────▼──────────┐\n                │ @ricardoqmd/        │\n                │   auth-keycloak     │   ← keycloak-js adapter\n                └─────────────────────┘\n```\n\nThe split lets you swap the IDP adapter (Keycloak today, Auth0/Cognito tomorrow) without touching your Next.js app.\n\n## Packages\n\n| Package                          | Description                                              | Version |\n| -------------------------------- | -------------------------------------------------------- | ------- |\n| `@ricardoqmd/auth-core`          | Framework-agnostic XState machine + types                | [![npm](https://img.shields.io/npm/v/@ricardoqmd/auth-core)](https://www.npmjs.com/package/@ricardoqmd/auth-core) |\n| `@ricardoqmd/auth-keycloak`      | Keycloak adapter using `keycloak-js`                     | [![npm](https://img.shields.io/npm/v/@ricardoqmd/auth-keycloak)](https://www.npmjs.com/package/@ricardoqmd/auth-keycloak) |\n| `@ricardoqmd/auth-nextjs`        | Next.js client-side bindings (`AuthProvider`, `useAuth`) | [![npm](https://img.shields.io/npm/v/@ricardoqmd/auth-nextjs)](https://www.npmjs.com/package/@ricardoqmd/auth-nextjs) |\n| `@ricardoqmd/auth-nextjs-ssr`    | Middleware + JWT validation (server-side)                | 🔜 planned |\n\n## Installation\n\n```bash\nnpm install @ricardoqmd/auth-nextjs @ricardoqmd/auth-keycloak @ricardoqmd/auth-core\n# peer dependencies\nnpm install keycloak-js react react-dom next\n```\n\nQuick start:\n\n```tsx\n// providers.tsx (Client Component)\n\"use client\";\nimport { AuthProvider } from \"@ricardoqmd/auth-nextjs\";\nimport { createKeycloakProvider } from \"@ricardoqmd/auth-keycloak\";\n\nconst provider = createKeycloakProvider({\n  config: { url: \"https://kc.example.com\", realm: \"my-realm\", clientId: \"my-app\" },\n});\n\nexport function Providers({ children }: { children: React.ReactNode }) {\n  return \u003cAuthProvider provider={provider}\u003e{children}\u003c/AuthProvider\u003e;\n}\n```\n\n```tsx\n// Any Client Component inside \u003cProviders\u003e\nimport { useAuth } from \"@ricardoqmd/auth-nextjs\";\nimport { hasResourceRole } from \"@ricardoqmd/auth-keycloak\";\nimport type { KeycloakIdpClaims } from \"@ricardoqmd/auth-keycloak\";\n\nexport function Dashboard() {\n  const { user, logout, hasRole, idpClaims } = useAuth\u003cKeycloakIdpClaims\u003e();\n\n  return (\n    \u003c\u003e\n      \u003cp\u003eWelcome, {user?.preferred_username}\u003c/p\u003e\n      {hasRole(\"admin\") \u0026\u0026 \u003cAdminPanel /\u003e}\n      {hasResourceRole(idpClaims, \"my-app\", \"editor\") \u0026\u0026 \u003cEditButton /\u003e}\n      \u003cbutton onClick={logout}\u003eSign out\u003c/button\u003e\n    \u003c/\u003e\n  );\n}\n```\n\n\u003e Sign-in on demand (`login()`), structured error handling (`AuthError.code`), and\n\u003e public/protected route patterns are documented in\n\u003e [`@ricardoqmd/auth-nextjs`](./packages/auth-nextjs/README.md).\n\n## Local development\n\n### Prerequisites\n\n- Node.js \u003e= 18.18\n- pnpm \u003e= 9 (`npm install -g pnpm`)\n- Docker + Docker Compose\n\n### Setup\n\n```bash\n# 1. Install dependencies\npnpm install\n\n# 2. Start Keycloak (ports 8080)\npnpm kc:up\n\n# 3. Wait ~30s for Keycloak to import the realm, then visit:\n#    http://localhost:8080/admin   (admin / admin)\n#    Realm: demo\n\n# 4. Configure the demo app\ncp apps/demo/.env.example apps/demo/.env.local\n\n# 5. Run packages in watch mode + the demo app\npnpm dev          # builds packages on change\npnpm demo         # in another terminal — http://localhost:3000\n```\n\n### Demo users\n\n| Username | Password   | Realm roles  | Client roles (`demo-app`) |\n| -------- | ---------- | ------------ | ------------------------- |\n| ricardo  | password   | admin, user  | editor, viewer            |\n| viewer   | password   | user         | viewer                    |\n\n### Useful scripts\n\n```bash\npnpm kc:up        # start Keycloak\npnpm kc:down      # stop Keycloak\npnpm kc:logs      # tail Keycloak logs\npnpm kc:reset     # full reset (drops volume → re-imports realm)\npnpm build        # build all packages\npnpm test         # run tests\npnpm typecheck    # type-check all packages\npnpm changeset    # record a release-worthy change\n```\n\n## Roadmap\n\nAll three packages are published and **stable (1.0)** — the public API is frozen\nand follows SemVer ([ADR-009](./docs/decisions/009-freeze-public-api-for-1.0.md)),\nand was validated end-to-end on real infrastructure\n([ADR-010](./docs/decisions/010-refine-1.0-consumer-gate.md)). SSR, additional IDP\nadapters, and Vue bindings are post-1.0 and demand-driven.\n\nFull plan: [`ROADMAP.md`](./ROADMAP.md) · Rationale: [ADR-006](./docs/decisions/006-harden-before-expand.md).\n\n## License\n\nMIT © ricardoqmd\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fricardoqmd%2Fauth","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fricardoqmd%2Fauth","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fricardoqmd%2Fauth/lists"}