{"_id":"@atlanhq/atlan-auth","_rev":"3-4baecc63fcc773e45cea68e975c4b51d","name":"@atlanhq/atlan-auth","dist-tags":{"latest":"2.0.0"},"versions":{"1.0.0":{"name":"@atlanhq/atlan-auth","version":"1.0.0","keywords":["atlan","auth","authentication","sdk","oauth","keycloak","oidc","embed","iframe","integration"],"author":{"name":"Atlan Technologies Pvt. Ltd."},"license":"ISC","_id":"@atlanhq/atlan-auth@1.0.0","maintainers":[{"name":"atlanyatin","email":"yatin@atlan.com"},{"name":"amit-atlan","email":"amit@atlan.com"},{"name":"atlan-engineering","email":"engineering@atlan.com"}],"homepage":"https://github.com/atlanhq/blaze#readme","bugs":{"url":"https://github.com/atlanhq/blaze/issues"},"dist":{"shasum":"eda7dc8341da7ec2b0f1c6eb528d2f8991080401","tarball":"https://registry.npmjs.org/@atlanhq/atlan-auth/-/atlan-auth-1.0.0.tgz","fileCount":33,"integrity":"sha512-E/XhoEAFuHMjBM0QSaoG0dEsF5VR5MOukazBXxu3Z9tf0qcbtYfxY74aGjxqMv0e1qZZmIpmON/OHcwe4dexRw==","signatures":[{"sig":"MEQCIAVl4KiQiIExMccYijNYSJyUNld5ZnQnXd+WGWDQCIvwAiBcHEkgccyqTU/WmdRnkrk4fVn4zwSXbVW7kfaECdZQUQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":310889},"main":"./dist/index.cjs","_from":"file:atlanhq-atlan-auth-1.0.0.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","browser":"./dist/atlan-auth.umd.min.js","private":false,"scripts":{"dev":"vite build --watch","test":"vitest","build":"vite build","format":"prettier --config ../../.prettierrc.cjs --write .","test:ui":"vitest --ui","test:watch":"vitest --watch","test:coverage":"vitest run --coverage"},"_npmUser":{"name":"atlan-engineering","email":"engineering@atlan.com"},"_resolved":"/private/var/folders/wk/802c9gmj2yj5lw1fh1dcmmgw0000gn/T/03bbcd8da9fb365b459992ce88785d00/atlanhq-atlan-auth-1.0.0.tgz","_integrity":"sha512-E/XhoEAFuHMjBM0QSaoG0dEsF5VR5MOukazBXxu3Z9tf0qcbtYfxY74aGjxqMv0e1qZZmIpmON/OHcwe4dexRw==","repository":{"url":"git+https://github.com/atlanhq/blaze.git","type":"git"},"_npmVersion":"11.6.2","description":"Official Atlan Authentication SDK for seamless external app integration","directories":{},"_nodeVersion":"24.11.1","dependencies":{"keycloak-js":"^25.0.6"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^6.3.6","jsdom":"^24.0.0","vitest":"^2.0.0","happy-dom":"^14.0.0","@vitest/ui":"^2.0.0","typescript":"^5.3.2","@types/node":"^20.11.0","vite-plugin-dts":"^4.5.0","@vitest/coverage-v8":"^2.0.0"},"peerDependencies":{"keycloak-js":"^25.0.6"},"peerDependenciesMeta":{"keycloak-js":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/atlan-auth_1.0.0_1769181030016_0.03072676627036186","host":"s3://npm-registry-packages-npm-production"}},"2.0.0":{"name":"@atlanhq/atlan-auth","version":"2.0.0","keywords":["atlan","auth","authentication","sdk","oauth","keycloak","oidc","embed","iframe","integration"],"author":{"name":"Atlan Technologies Pvt. Ltd."},"license":"ISC","_id":"@atlanhq/atlan-auth@2.0.0","maintainers":[{"name":"atlanyatin","email":"yatin@atlan.com"},{"name":"amit-atlan","email":"amit@atlan.com"},{"name":"atlan-engineering","email":"engineering@atlan.com"}],"homepage":"https://github.com/atlanhq/blaze#readme","bugs":{"url":"https://github.com/atlanhq/blaze/issues"},"dist":{"shasum":"ae9b1d69f75f1a47bdb46d493f88ee055183dfee","tarball":"https://registry.npmjs.org/@atlanhq/atlan-auth/-/atlan-auth-2.0.0.tgz","fileCount":33,"integrity":"sha512-8oM6kbyTWLnI4omR5nFAipDgXLqr4ZTOQ7XOFb0XwlrTJndKy2stqsrLiz3kSKQT9jfOXi/5REUfoxC4YFnsUw==","signatures":[{"sig":"MEYCIQCdBHdQ7wgwN20h7zYibpaoe0fitT35hfJM26gmVHqj4gIhAM3mzHK8LNeKEGwFGUjJfWm9/wXyKPuwIhC7rsG4WZZO","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":836372},"main":"./dist/index.cjs","_from":"file:atlanhq-atlan-auth-2.0.0.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","browser":"./dist/atlan-auth.umd.min.js","private":false,"scripts":{"dev":"vite build --watch","test":"vitest","build":"vite build","format":"prettier --config ../../.prettierrc.cjs --write .","test:ui":"vitest --ui","test:watch":"vitest --watch","test:coverage":"vitest run --coverage"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:a5a72a3e-561d-4d76-86bc-5182358af486"}},"_resolved":"/tmp/6b6c343eb609e82bde839c28925f2755/atlanhq-atlan-auth-2.0.0.tgz","_integrity":"sha512-8oM6kbyTWLnI4omR5nFAipDgXLqr4ZTOQ7XOFb0XwlrTJndKy2stqsrLiz3kSKQT9jfOXi/5REUfoxC4YFnsUw==","repository":{"url":"git+https://github.com/atlanhq/blaze.git","type":"git"},"_npmVersion":"11.10.0","description":"Official Atlan Authentication SDK for seamless external app integration","directories":{},"_nodeVersion":"22.22.0","dependencies":{"keycloak-js":"^25.0.6"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^6.3.6","jsdom":"^24.0.0","vitest":"^2.0.0","happy-dom":"^14.0.0","@vitest/ui":"^2.0.0","typescript":"^5.3.2","@types/node":"^20.11.0","vite-plugin-dts":"^4.5.0","@vitest/coverage-v8":"^2.0.0"},"peerDependencies":{"keycloak-js":"^25.0.6"},"peerDependenciesMeta":{"keycloak-js":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/atlan-auth_2.0.0_1771418360031_0.6386098541211489","host":"s3://npm-registry-packages-npm-production"}}},"time":{"created":"2026-01-23T15:10:29.946Z","modified":"2026-07-31T06:10:05.618Z","1.0.0":"2026-01-23T15:10:30.196Z","2.0.0":"2026-02-18T12:39:20.236Z"},"bugs":{"url":"https://github.com/atlanhq/blaze/issues"},"author":{"name":"Atlan Technologies Pvt. Ltd."},"license":"ISC","homepage":"https://github.com/atlanhq/blaze#readme","keywords":["atlan","auth","authentication","sdk","oauth","keycloak","oidc","embed","iframe","integration"],"repository":{"url":"git+https://github.com/atlanhq/blaze.git","type":"git"},"description":"Official Atlan Authentication SDK for seamless external app integration","maintainers":[{"email":"amit@atlan.com","name":"amit-atlan"},{"email":"engineering@atlan.com","name":"atlan-engineering"}],"readme":"# @atlanhq/atlan-auth\n\nOfficial Authentication SDK for integrating external applications with Atlan, featuring seamless authentication via Keycloak.\n\n## Features\n\n- 🔐 **Automatic Authentication**: Auto-detects embedded vs standalone mode\n- 🎫 **Keycloak Integration**: Uses Keycloak with PKCE for secure, browser-safe auth\n- 🔄 **Auto Token Refresh**: Tokens refresh automatically before expiration\n- 🎯 **Type-Safe**: Full TypeScript support\n- 📦 **Small Bundle**: ~52KB gzipped (includes Keycloak)\n- 🚀 **Zero Config**: Just provide your Atlan origin URL\n\n## Installation\n\n### CDN (For plain HTML/JS apps)\n\n```html\n<script src=\"https://unpkg.com/@atlanhq/atlan-auth@latest/dist/atlan-auth.umd.min.js\"></script>\n<script>\n    const { AtlanAuth } = window.AtlanAuthSDK\n    // SDK ready to use!\n</script>\n```\n\n### npm (For React/Vue/Angular apps)\n\n```bash\n# Add to .npmrc:\n# @atlanhq:registry=https://npm.pkg.github.com\n\nnpm install @atlanhq/atlan-auth keycloak-js\n# or\nyarn add @atlanhq/atlan-auth keycloak-js\n# or\npnpm add @atlanhq/atlan-auth keycloak-js\n```\n\n> **Note**: When using npm, you need to install `keycloak-js` as a peer dependency. CDN users get it bundled automatically.\n\n## Quick Start\n\nThe SDK works in two modes automatically:\n\n1. **Embedded Mode**: When your app runs inside an Atlan iframe (production)\n2. **Standalone Mode**: When developing locally or running outside Atlan\n\n### Embedded Mode (Inside Atlan iframe)\n\n```javascript\nconst atlan = new AtlanAuth({\n    origin: 'https://your-tenant.atlan.com',\n})\n\nawait atlan.init()\n// ✅ Instant authentication via postMessage\nconsole.log('User:', atlan.getUser())\nconsole.log('Token:', atlan.getToken())\n```\n\n### Standalone Mode (Local development)\n\n```javascript\nconst atlan = new AtlanAuth({\n    origin: 'http://localhost:3333', // Your local Atlan instance\n})\n\nawait atlan.init()\n// 🔄 Redirects to Atlan login page\n// ✅ Returns after successful authentication\nconsole.log('User:', atlan.getUser())\n```\n\n**What happens in standalone mode:**\n\n1. SDK detects you're not in an iframe\n2. **Silently checks** for existing session (no redirect if already logged in!)\n3. If no session: Redirects to `{origin}/auth/realms/default/login`\n4. You log in with your Atlan credentials\n5. Keycloak redirects back to your app with auth code\n6. SDK exchanges code for token (PKCE flow)\n7. Your app is now authenticated!\n\n> **Note**: The SDK uses \"silent SSO check\" by default, so returning users won't see a redirect flash!\n\n## Complete Example\n\n```javascript\n// CDN usage\nconst { AtlanAuth } = window.AtlanAuthSDK\n\n// npm usage: import { AtlanAuth } from '@atlanhq/atlan-auth'\n\nconst atlan = new AtlanAuth({\n    origin: 'https://jpmc.atlan.com',\n    onReady: (context) => {\n        console.log('✅ SDK Ready!')\n        console.log('Mode:', context.mode) // 'embedded' or 'standalone'\n        console.log('User:', context.user)\n        console.log('Tenant:', context.tenant)\n    },\n    onError: (error) => {\n        console.error('❌ SDK Error:', error)\n    },\n    debug: true, // Enable console logging\n})\n\n// Initialize (async)\ntry {\n    await atlan.init()\n\n    // Make API calls\n    const assets = await atlan.api.get('/api/meta/search')\n    console.log('Assets:', assets.data)\n} catch (error) {\n    console.error('Initialization failed:', error)\n}\n```\n\n## Handling Loading States\n\nTo prevent a flash of content before authentication, use CSS-first loading patterns:\n\n### Recommended Pattern (CSS-First)\n\n```html\n<head>\n    <style>\n        /* Hide app until authenticated */\n        body.loading #app {\n            display: none;\n        }\n        body.loading::before {\n            content: '🔐 Authenticating...';\n            display: flex;\n            justify-content: center;\n            align-items: center;\n            height: 100vh;\n        }\n    </style>\n</head>\n<body class=\"loading\">\n    <div id=\"app\">\n        <!-- Your app content -->\n    </div>\n\n    <script src=\"https://unpkg.com/@atlanhq/atlan-auth@latest/dist/atlan-auth.umd.min.js\"></script>\n    <script>\n        const { AtlanAuth } = window.AtlanAuthSDK\n\n        const atlan = new AtlanAuth({\n            origin: 'https://your-tenant.atlan.com',\n            onReady: (context) => {\n                // Remove loading state\n                document.body.classList.remove('loading')\n            },\n        })\n\n        atlan.init()\n    </script>\n</body>\n```\n\n**Why CSS-first?** CSS loads before JavaScript, so the loading state is visible from the first paint—no flash!\n\n### Alternative: Using Callbacks\n\n```javascript\nconst atlan = new AtlanAuth({\n    origin: 'https://your-tenant.atlan.com',\n    onStateChange: (state) => {\n        if (state === 'initializing') {\n            showLoader()\n        } else if (state === 'authenticated') {\n            hideLoader()\n        }\n    },\n})\n\nawait atlan.init()\n```\n\n**Note:** In standalone mode, the SDK uses \"silent SSO check\"—returning users authenticate instantly without redirect!\n\n## API Reference\n\n### Constructor\n\n```typescript\nnew AtlanAuth(config: AtlanAuthConfig)\n```\n\n**Config Options:**\n\n```typescript\ninterface AtlanAuthConfig {\n    origin: string // Atlan instance URL (required)\n    onReady?: (context: AuthContext) => void // Success callback\n    onError?: (error: AuthError) => void // Error callback\n    onStateChange?: (state: AuthState, prevState: AuthState) => void // State change callback\n    onTokenRefresh?: (token: string) => void // Token refresh callback\n    onTokenExpired?: () => void // Token expired callback\n    onLogout?: () => void // Logout callback\n    keycloak?: KeycloakConfig // Keycloak customization\n    interceptors?: Interceptors // Request/response interceptors\n    debug?: boolean // Enable debug logging (default: false)\n}\n```\n\n### Methods\n\n#### `init(): Promise<AuthContext>`\n\nInitialize the SDK and authenticate. Must be called before using other methods.\n\n```javascript\nconst context = await atlan.init()\n```\n\nReturns:\n\n```typescript\ninterface AuthContext {\n    token: string\n    user: User\n    tenant: string\n    mode: 'embedded' | 'standalone'\n}\n```\n\n#### `getToken(): string | null`\n\nGet the current authentication token.\n\n```javascript\nconst token = atlan.getToken()\n```\n\n#### `getUser(): User | null`\n\nGet the current user information.\n\n```javascript\nconst user = atlan.getUser()\n// { id, username, email, firstName, lastName }\n```\n\n#### `getTenant(): string | null`\n\nGet the current tenant name.\n\n```javascript\nconst tenant = atlan.getTenant()\n// e.g., 'jpmc'\n```\n\n#### `getMode(): 'embedded' | 'standalone'`\n\nGet the current authentication mode.\n\n```javascript\nconst mode = atlan.getMode()\n```\n\n#### `isLoading(): boolean`\n\nCheck if the SDK is currently loading or initializing. Safe to call before `init()`.\n\n```javascript\nconst atlan = new AtlanAuth({ origin: '...' })\n\nif (atlan.isLoading()) {\n    console.log('SDK is initializing...')\n}\n```\n\nReturns `true` if the SDK state is `initializing` or `refreshing`.\n\n#### `willRedirect(): boolean`\n\nCheck if the SDK will redirect to the login page (standalone mode only). Safe to call before `init()`.\n\n```javascript\nconst atlan = new AtlanAuth({ origin: '...' })\n\nif (atlan.willRedirect()) {\n    // Show loading UI - redirect is about to happen\n    document.body.innerHTML = '<h2>Redirecting to login...</h2>'\n}\n\nawait atlan.init() // Will redirect if willRedirect() returned true\n```\n\nReturns:\n\n- `false` in embedded mode (never redirects)\n- `false` in standalone mode if returning from login (has auth code)\n- `true` in standalone mode if no active session (will redirect)\n\n**Use case**: Display appropriate loading UI before the redirect happens, preventing a flash of your app's content.\n\n#### `isInitialized(): boolean`\n\nCheck if SDK is initialized.\n\n```javascript\nif (atlan.isInitialized()) {\n    // SDK ready\n}\n```\n\n#### `getState(): AuthState`\n\nGet the current authentication state.\n\n```javascript\nconst state = atlan.getState()\n// 'idle' | 'initializing' | 'authenticated' | 'refreshing' | 'error' | 'logged_out'\n```\n\n#### `logout(options?): Promise<void>`\n\nLogout from the authentication provider.\n\n```javascript\nawait atlan.logout({ redirectUri: 'https://myapp.com' })\n```\n\n#### `retry(): Promise<AuthContext>`\n\nRetry initialization after an error.\n\n```javascript\ntry {\n    await atlan.init()\n} catch (error) {\n    // Retry after error\n    await atlan.retry()\n}\n```\n\n#### `reset(): void`\n\nReset SDK to idle state.\n\n```javascript\natlan.reset()\n```\n\n#### `isTokenExpired(): boolean`\n\nCheck if the current token is expired.\n\n```javascript\nif (atlan.isTokenExpired()) {\n    await atlan.retry()\n}\n```\n\n#### `getTokenExpiry(): Date | null`\n\nGet the token expiry date.\n\n```javascript\nconst expiry = atlan.getTokenExpiry()\nconsole.log('Token expires at:', expiry)\n```\n\n### API Client\n\nThe SDK includes a built-in API client for making authenticated requests to Atlan:\n\n#### `api.get(url, options?)`\n\n```javascript\nconst response = await atlan.api.get('/api/service/whoami')\nconsole.log(response.data)\n```\n\n#### `api.post(url, data, options?)`\n\n```javascript\nconst response = await atlan.api.post('/api/meta/entity', {\n    typeName: 'Table',\n    attributes: { name: 'my_table' },\n})\n```\n\n#### `api.put(url, data, options?)`\n\n```javascript\nconst response = await atlan.api.put('/api/meta/entity/guid/abc-123', {\n    attributes: { description: 'Updated' },\n})\n```\n\n#### `api.delete(url, options?)`\n\n```javascript\nawait atlan.api.delete('/api/meta/entity/guid/abc-123')\n```\n\n**API Response:**\n\n```typescript\ninterface ApiResponse<T> {\n    data: T\n    status: number\n    headers: Record<string, string>\n}\n```\n\n## Framework Examples\n\n### React\n\n```jsx\nimport { AtlanAuth } from '@atlanhq/atlan-auth'\nimport { useEffect, useState } from 'react'\n\nfunction App() {\n    const [atlan, setAtlan] = useState(null)\n    const [user, setUser] = useState(null)\n\n    useEffect(() => {\n        const sdk = new AtlanAuth({\n            origin: 'https://your-tenant.atlan.com',\n            onReady: (context) => {\n                setUser(context.user)\n            },\n            onError: (error) => {\n                console.error(error)\n            },\n        })\n\n        sdk.init().then(() => setAtlan(sdk))\n    }, [])\n\n    if (!user) return <div>Loading...</div>\n\n    return (\n        <div>\n            <h1>Welcome, {user.username}!</h1>\n        </div>\n    )\n}\n```\n\n### Vue 3\n\n```vue\n<template>\n    <div v-if=\"user\">\n        <h1>Welcome, {{ user.username }}!</h1>\n    </div>\n    <div v-else>Loading...</div>\n</template>\n\n<script setup>\n    import { AtlanAuth } from '@atlanhq/atlan-auth'\n    import { ref, onMounted } from 'vue'\n\n    const atlan = ref(null)\n    const user = ref(null)\n\n    onMounted(async () => {\n        const sdk = new AtlanAuth({\n            origin: 'https://your-tenant.atlan.com',\n            onReady: (context) => {\n                user.value = context.user\n            },\n        })\n\n        await sdk.init()\n        atlan.value = sdk\n    })\n</script>\n```\n\n### Plain HTML/JS\n\n```html\n<!DOCTYPE html>\n<html>\n    <head>\n        <title>My Atlan App</title>\n    </head>\n    <body>\n        <div id=\"app\">Loading...</div>\n\n        <script src=\"https://unpkg.com/@atlanhq/atlan-auth@latest/dist/atlan-auth.umd.min.js\"></script>\n        <script>\n            const { AtlanAuth } = window.AtlanAuthSDK\n\n            const atlan = new AtlanAuth({\n                origin: 'https://your-tenant.atlan.com',\n                onReady: (context) => {\n                    document.getElementById('app').innerHTML = `\n                    <h1>Welcome, ${context.user.username}!</h1>\n                `\n                },\n            })\n\n            atlan.init()\n        </script>\n    </body>\n</html>\n```\n\n## How It Works\n\n### Embedded Mode (Production)\n\n```\n┌─────────────────────────────────────────┐\n│ Atlan Frontend (localhost:3333)        │\n│                                         │\n│  ┌─────────────────────────────────┐   │\n│  │ Your App (in iframe)            │   │\n│  │                                 │   │\n│  │  1. SDK sends: IFRAME_READY ────┼───┤\n│  │                                 │   │\n│  │  2. ────────── ATLAN_AUTH_CONTEXT   │\n│  │     (token, user via postMessage) ◄─┤\n│  │                                 │   │\n│  │  3. ✅ Instant authentication   │   │\n│  └─────────────────────────────────┘   │\n└─────────────────────────────────────────┘\n```\n\n### Standalone Mode (Development)\n\n```\n┌─────────────────────────────────────────┐\n│ Your App (localhost:8000)               │\n│                                         │\n│  1. SDK detects standalone mode         │\n│  2. Redirects to Keycloak login ────────┼─┐\n└─────────────────────────────────────────┘ │\n                                            │\n┌─────────────────────────────────────────┐ │\n│ Atlan Keycloak (localhost:3333/auth)   ◄─┘\n│                                         │\n│  3. User logs in                        │\n│  4. Redirects back with auth code ──────┼─┐\n└─────────────────────────────────────────┘ │\n                                            │\n┌─────────────────────────────────────────┐ │\n│ Your App (localhost:8000)              ◄─┘\n│                                         │\n│  5. SDK exchanges code for token (PKCE) │\n│  6. ✅ Authenticated                     │\n└─────────────────────────────────────────┘\n```\n\n## Security\n\n- ✅ **No client secrets in browser**: Uses Keycloak's PKCE flow (Authorization Code Flow with Proof Key for Code Exchange)\n- ✅ **Secure token storage**: Tokens managed by Keycloak, not stored in localStorage\n- ✅ **Automatic refresh**: Tokens refresh before expiration\n- ✅ **Origin validation**: postMessage communication validates origins\n\n## Troubleshooting\n\n### Redirect loop in standalone mode\n\nMake sure your Atlan instance is running and accessible at the `origin` URL.\n\n### \"Not authenticated\" errors\n\n1. Check that `atlan.init()` completed successfully\n2. Verify your Atlan instance URL is correct\n3. Enable debug logging: `debug: true`\n\n### TypeScript errors\n\nIf using npm, ensure `keycloak-js` is installed:\n\n```bash\nnpm install keycloak-js\n```\n\n### CORS errors\n\nCORS errors only occur in standalone mode. Make sure:\n\n1. Your Atlan instance allows your app's origin\n2. You're running your app on the same domain as your Atlan instance (e.g., both on `localhost`)\n\n## License\n\nISC\n\n## Support\n\nFor issues and questions, please open an issue on [GitHub](https://github.com/atlanhq/blaze).\n","readmeFilename":"README.md"}