diff --git a/docs/content/en/providers/asgardeo.md b/docs/content/en/providers/asgardeo.md new file mode 100644 index 000000000..2c2d2b0ca --- /dev/null +++ b/docs/content/en/providers/asgardeo.md @@ -0,0 +1,69 @@ +--- +title: Asgardeo +description: Asgardeo is an identity-as-a-service platform by WSO2 built on OpenID Connect. +position: 32 +category: Providers +--- + +[Source Code](https://github.com/nuxt-community/auth-module/blob/dev/src/providers/asgardeo.ts) + +[Asgardeo](https://wso2.com/asgardeo/) is an identity-as-a-service (IDaaS) platform by WSO2 with first-class OpenID Connect support. + +## Usage + +```js +auth: { + strategies: { + asgardeo: { + clientId: '', + issuer: 'https://api.asgardeo.io/t/', + scope: ['openid', 'profile', 'email'] + } + } +} +``` + +Anywhere in your application logic: + +```js +this.$auth.loginWith('asgardeo') +``` + +💁 This provider is based on the [openIDConnect scheme](../schemes/openIDConnect) and supports all of its options. + +## Obtaining `clientId` and `issuer` + +`clientId` and `issuer` are **REQUIRED**. + +- `clientId` — register a **Single-Page Application** in the [Asgardeo Console](https://console.asgardeo.io) and copy its client ID. Public clients use the Authorization Code flow with PKCE, so no client secret is stored in the bundle. +- `issuer` — your Asgardeo organization URL, `https://api.asgardeo.io/t/`, where `` is your organization name. The provider uses this value to resolve the Asgardeo OpenID Connect discovery endpoint automatically. + +The provider derives the OpenID Connect discovery document from the issuer: + +``` +/oauth2/token/.well-known/openid-configuration +``` + +All endpoints (authorization, token, userInfo, logout) are then resolved automatically from that document, so you normally do not set them by hand. + +## Redirect URLs + +In the Asgardeo Console, add your callback route to the application's **Authorized redirect URLs** (defaults to `/login`, e.g. `http://localhost:3000/login`). To be redirected back after logout, also add your logout target and set `logoutRedirectUri`: + +```js +auth: { + strategies: { + asgardeo: { + clientId: '', + issuer: 'https://api.asgardeo.io/t/', + logoutRedirectUri: 'http://localhost:3000' + } + } +} +``` + +Then log out with: + +```js +await this.$auth.logout() +``` diff --git a/src/providers/asgardeo.ts b/src/providers/asgardeo.ts new file mode 100644 index 000000000..8bd38d98c --- /dev/null +++ b/src/providers/asgardeo.ts @@ -0,0 +1,25 @@ +import type { ProviderOptions, ProviderPartialOptions } from '../types' +import type { OpenIDConnectSchemeOptions } from '../schemes' +import { assignDefaults } from '../utils/provider' + +export interface AsgardeoProviderOptions + extends ProviderOptions, + OpenIDConnectSchemeOptions { + issuer: string +} + +export function asgardeo( + // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types + _nuxt: any, + strategy: ProviderPartialOptions +): void { + const DEFAULTS: typeof strategy = { + scheme: 'openIDConnect', + endpoints: { + configuration: `${strategy.issuer}/oauth2/token/.well-known/openid-configuration` + }, + scope: ['openid', 'profile', 'email'] + } + + assignDefaults(strategy, DEFAULTS) +} diff --git a/src/providers/index.ts b/src/providers/index.ts index 5b5fb33ca..ecd084a56 100644 --- a/src/providers/index.ts +++ b/src/providers/index.ts @@ -1,3 +1,4 @@ +export * from './asgardeo' export * from './auth0' export * from './discord' export * from './facebook' diff --git a/test/asgardeo.test.ts b/test/asgardeo.test.ts new file mode 100644 index 000000000..e39134ce9 --- /dev/null +++ b/test/asgardeo.test.ts @@ -0,0 +1,57 @@ +import { asgardeo, AsgardeoProviderOptions } from '../src/providers/asgardeo' +import type { ProviderPartialOptions } from '../src/types' + +const ISSUER = 'https://api.asgardeo.io/t/example' + +const buildStrategy = ( + overrides: Partial> = {} +): ProviderPartialOptions => + ({ + clientId: 'test_client_id', + issuer: ISSUER, + ...overrides + } as ProviderPartialOptions) + +describe('asgardeo provider', () => { + test('defaults to the openIDConnect scheme', () => { + const strategy = buildStrategy() + asgardeo({}, strategy) + expect(strategy.scheme).toBe('openIDConnect') + }) + + test('derives the discovery document endpoint from the issuer', () => { + const strategy = buildStrategy() + asgardeo({}, strategy) + expect(strategy.endpoints.configuration).toBe( + `${ISSUER}/oauth2/token/.well-known/openid-configuration` + ) + }) + + test('applies the default OpenID Connect scope', () => { + const strategy = buildStrategy() + asgardeo({}, strategy) + expect(strategy.scope).toEqual(['openid', 'profile', 'email']) + }) + + test('merges a user-provided scope with default scope', () => { + const strategy = buildStrategy({ scope: ['openid', 'internal_login'] }) + asgardeo({}, strategy) + + expect(strategy.scope).toEqual([ + 'openid', + 'profile', + 'email', + 'openid', + 'internal_login' + ]) + }) + + test('does not override a user-provided configuration endpoint', () => { + const custom = 'https://example.com/custom/.well-known/openid-configuration' + const strategy = buildStrategy({ + endpoints: { configuration: custom } + }) + asgardeo({}, strategy) + expect(strategy.endpoints.configuration).toBe(custom) + }) +})