-
Notifications
You must be signed in to change notification settings - Fork 1
feat(oidc): add Microsoft Identity OpenID Connect provider #202
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,197 @@ | ||
| --- | ||
| title: Microsoft Identity | ||
| description: Add Microsoft Identity authorization provider to Aura Auth for authentication and authorization | ||
| --- | ||
|
|
||
| <Steps> | ||
|
|
||
| <Step> | ||
|
|
||
| ## Microsoft Identity OAuth App | ||
|
|
||
| ### Register the Application | ||
|
|
||
| The first step is to create and register an OAuth App on the Microsoft Identity developer console to obtain access to the user's resources. | ||
|
|
||
| </Step> | ||
|
|
||
| <Step> | ||
|
|
||
| ## Installation | ||
|
|
||
| Install the package using a package manager like `npm`, `pnpm`, or `yarn`: | ||
|
|
||
| ```npm | ||
| npm install @aura-stack/auth | ||
| ``` | ||
|
|
||
| </Step> | ||
|
|
||
| <Step> | ||
|
|
||
| ## Environment setup | ||
|
|
||
| Now, you must configure the environment variables required by Aura Auth, including the Microsoft Identity credentials and the encryption secrets. | ||
|
|
||
| ```bash title=".env" lineNumbers | ||
| # Aura Secrets | ||
| AURA_AUTH_SECRET="your-32-byte-secret" | ||
| AURA_AUTH_SALT="your-32-byte-salt" | ||
|
|
||
| # Microsoft Identity Credentials | ||
| AURA_AUTH_MICROSOFT_IDENTITY_CLIENT_ID="your_microsoft_identity_client_id" | ||
| AURA_AUTH_MICROSOFT_IDENTITY_CLIENT_SECRET="your_microsoft_identity_client_secret" | ||
| ``` | ||
|
|
||
| <Callout type="warn"> | ||
| **CRITICAL SECURITY WARNING:** The `AURA_AUTH_SECRET` and `AURA_AUTH_SALT` variables are used to encrypt and sign user sessions. | ||
| These MUST be securely generated, highly randomized strings consisting of at least 32 bytes to ensure adequate entropy. Never | ||
| hardcode these values in your repository. Use a secure generator (like `openssl rand -base64 32`) to create them, and store them | ||
| exclusively in your secure environment variables manager. | ||
| </Callout> | ||
|
|
||
| </Step> | ||
|
|
||
| <Step> | ||
|
|
||
| ## Configure the Auth Instance | ||
|
|
||
| Configure the `createAuth` instance inside an `auth.ts` file located at the root of your project. Ensure you explicitly export the `handlers`, `api`, and `jose` objects. | ||
|
|
||
| ```ts title="auth.ts" lineNumbers | ||
| import { createAuth } from "@aura-stack/auth" | ||
|
|
||
| export const auth = createAuth({ | ||
| oauth: ["microsoft-identity"], | ||
| }) | ||
|
|
||
| // Extract the required utilities | ||
| export const { handlers, api, jose } = auth | ||
| ``` | ||
|
|
||
| <Callout type="info"> | ||
| The `handlers` object contains mapping utilities for standard HTTP methods (`GET`, `POST`, `PATCH`) as well as a unified `ALL` | ||
| handler. This allows you to easily mount the authentication routes across any framework (Next.js, Elysia, Express, etc.). | ||
| </Callout> | ||
|
|
||
| </Step> | ||
|
|
||
| <Step> | ||
|
|
||
| ## Customizing the OAuth Provider | ||
|
|
||
| If you need to define custom scopes, change the response type, or map profile data differently, you can use the provider's factory function instead of a simple string identifier. | ||
|
|
||
| ```ts title="auth.ts" lineNumbers | ||
| import { createAuth } from "@aura-stack/auth" | ||
| import { microsoftIdentity } from "@aura-stack/auth/oauth/microsoft-identity" | ||
|
|
||
| export const auth = createAuth({ | ||
| oauth: [ | ||
| microsoftIdentity({ | ||
| authorize: { | ||
| params: { | ||
| // Override default scopes | ||
| scope: "read:user user:email", | ||
| }, | ||
| }, | ||
| }), | ||
| ], | ||
| }) | ||
|
|
||
| export const { handlers, api, jose } = auth | ||
| ``` | ||
|
|
||
| </Step> | ||
|
|
||
| <Step> | ||
|
|
||
| ## Sign In to Microsoft Identity (Client & Server) | ||
|
|
||
| There are multiple ways to trigger the sign-in flow depending on your ecosystem. | ||
|
|
||
| ### Sign-in Path (Direct Navigation) | ||
|
|
||
| The common route to trigger the auth flow natively without needing a client library is simply navigating the browser to: | ||
| `http://localhost:3000/auth/signIn/microsoft-identity` | ||
|
|
||
| --- | ||
|
|
||
| ### Client-Side (React, Vue, etc.) | ||
|
|
||
| You can utilize the `createAuthClient` utility to programmatically trigger sign-ins. You can also define a `redirectTo` destination. | ||
|
|
||
| <Callout type="warn"> | ||
| **Constraint Rule**: The `baseURL` passed into `createAuthClient` MUST exactly match the root domain and path where the HTTP | ||
| `handlers` expose their endpoints on the server. | ||
| </Callout> | ||
|
|
||
| ```ts title="components/Login.tsx" lineNumbers | ||
| import { createAuthClient } from "@aura-stack/auth/client" | ||
|
|
||
| export const authClient = createAuthClient({ | ||
| baseURL: "http://localhost:3000/auth", | ||
| }) | ||
|
|
||
| const triggerSignIn = async () => { | ||
| await authClient.signIn("microsoft-identity", { | ||
| redirectTo: "/dashboard", | ||
| }) | ||
| } | ||
| ``` | ||
|
|
||
| --- | ||
|
|
||
| ### Server-Side (Next.js Actions, Remix Loaders, etc.) | ||
|
|
||
| For environments supporting server-side actions, use the programmatic `api.signIn` method securely. | ||
|
|
||
| ```ts title="actions.ts" lineNumbers | ||
| import { api } from "./auth" | ||
|
|
||
| export const serverSignIn = async () => { | ||
| const response = await api.signIn("microsoft-identity", { | ||
| redirectTo: "http://localhost:3000/dashboard", | ||
| }) | ||
|
|
||
| // Example returning redirect location | ||
| return response.headers.get("Location") | ||
| } | ||
| ``` | ||
|
|
||
| --- | ||
|
|
||
| ### Session Retrieval | ||
|
|
||
| After a user successfully signs in, you can retrieve their session data securely. | ||
|
|
||
| **Client-Side:** | ||
|
|
||
| ```ts | ||
| const session = await authClient.getSession() | ||
| console.log(session?.user) // The authenticated Microsoft Identity user profile | ||
| ``` | ||
|
|
||
| **Server-Side:** | ||
|
|
||
| ```ts | ||
| // Note: You must pass the native Web Request object or Headers! | ||
| const session = await api.getSession(request) | ||
| console.log(session?.user) // Safely retrieved backend session | ||
| ``` | ||
|
|
||
| </Step> | ||
|
|
||
| </Steps> | ||
|
|
||
| --- | ||
|
|
||
| ## Resources | ||
|
|
||
| - [RFC - The OAuth 2.0 Authorization Framework](https://datatracker.ietf.org/doc/html/rfc6749) | ||
| - [Microsoft Identity Platform - OpenID Connect](https://learn.microsoft.com/en-us/entra/identity-platform/v2-protocols-oidc) | ||
| - [Microsoft Identity Platform - OpenID Connect Metadata Document](https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration) | ||
| - [Microsoft Identity Platform - OpenID Connect ID Token Claims](https://learn.microsoft.com/en-us/entra/identity-platform/id-tokens) | ||
| - [Microsoft Identity Platform - OpenID Connect Scopes](https://learn.microsoft.com/en-us/entra/identity-platform/scopes-oidc) | ||
| - [Microsoft Identity Platform - UserInfo Endpoint](https://learn.microsoft.com/en-us/entra/identity-platform/userinfo) | ||
| - [Microsoft Identity Platform - ID Token Claims Reference](https://learn.microsoft.com/en-us/entra/identity-platform/id-token-claims-reference) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| import type { OpenIDProvider, User } from "@/@types/index.ts" | ||
|
|
||
| /** | ||
| * @see [Microsoft Identity Platform - UserInfo Endpoint](https://learn.microsoft.com/en-us/entra/identity-platform/userinfo) | ||
| * @see [Microsoft Identity Platform - ID Token Claims Reference](https://learn.microsoft.com/en-us/entra/identity-platform/id-token-claims-reference) | ||
| */ | ||
| export interface MicrosoftIdentityProfile { | ||
| aud: string | ||
| iss: string | ||
| iat: number | ||
| idp: string | ||
| nbf: number | ||
| exp: number | ||
| c_hash: string | ||
| at_hash: string | ||
| preferred_username: string | ||
| email: string | ||
| nonce: string | ||
| oid: string | ||
| roles: string[] | ||
| rh: string | ||
| sub: string | ||
| sid: string | ||
| unique_name: string | ||
| uti: string | ||
| ver: string | ||
| hasgroups: boolean | ||
| name: string | ||
| family_name: string | ||
| given_name: string | ||
| picture: string | ||
| } | ||
|
|
||
| export const MICROSOFT_IDENTITY_ISSUER = "https://login.microsoftonline.com/:tenantId/v2.0" | ||
|
|
||
| /** | ||
| * Microsoft Identity Platform OpenID Connect Provider | ||
| * | ||
| * @see [Microsoft Identity Platform - OpenID Connect](https://learn.microsoft.com/en-us/entra/identity-platform/v2-protocols-oidc) | ||
| * @see [Microsoft Identity Platform - OpenID Connect Metadata Document](https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration) | ||
| * @see [Microsoft Identity Platform - OpenID Connect ID Token Claims](https://learn.microsoft.com/en-us/entra/identity-platform/id-tokens) | ||
| * @see [Microsoft Identity Platform - OpenID Connect Scopes](https://learn.microsoft.com/en-us/entra/identity-platform/scopes-oidc) | ||
| * @see [Microsoft Identity Platform - UserInfo Endpoint](https://learn.microsoft.com/en-us/entra/identity-platform/userinfo) | ||
| * @see [Microsoft Identity Platform - ID Token Claims Reference](https://learn.microsoft.com/en-us/entra/identity-platform/id-token-claims-reference) | ||
| */ | ||
| export const microsoftIdentity = <DefaultUser extends User = User>( | ||
| options?: Partial<OpenIDProvider<MicrosoftIdentityProfile, DefaultUser, typeof MICROSOFT_IDENTITY_ISSUER>> | ||
| ): OpenIDProvider<MicrosoftIdentityProfile, DefaultUser, typeof MICROSOFT_IDENTITY_ISSUER> => { | ||
| return { | ||
| id: "microsoft-identity", | ||
| name: "Microsoft Identity", | ||
| issuer: MICROSOFT_IDENTITY_ISSUER, | ||
|
Comment on lines
+34
to
+52
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Description: Check whether Microsoft Identity issuer placeholders are interpolated anywhere.
rg -n --fixed-strings ':tenantId' .
rg -n 'MICROSOFT_IDENTITY_ISSUER|microsoft-identity|tenantId|issuer.*replace' .Repository: aura-stack-ts/auth Length of output: 1805 Fix the unresolved The Update the export const microsoftIdentity = <DefaultUser extends User = User>(
options?: { tenantId?: string } & Partial<OpenIDProvider<MicrosoftIdentityProfile, DefaultUser, string>>
): OpenIDProvider<MicrosoftIdentityProfile, DefaultUser, string> => {
const tenant = options?.tenantId ?? "common";
const issuer = `https://login.microsoftonline.com/${tenant}/v2.0`;
return {
id: "microsoft-identity",
name: "Microsoft Identity",
issuer,
// ...
};
};Update the documentation in 🤖 Prompt for AI Agents |
||
| profile: (profile) => | ||
| ({ | ||
| sub: profile.sub, | ||
| name: profile.name, | ||
| email: profile.email, | ||
| image: profile.picture, | ||
| }) as DefaultUser, | ||
| ...options, | ||
| } as OpenIDProvider<MicrosoftIdentityProfile, DefaultUser, typeof MICROSOFT_IDENTITY_ISSUER> | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -104,4 +104,4 @@ | |
| "elysia": ">=1.0.0" | ||
| }, | ||
| "packageManager": "pnpm@10.15.0" | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -109,4 +109,4 @@ | |
| "express": ">=4.0.0" | ||
| }, | ||
| "packageManager": "pnpm@10.15.0" | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -107,4 +107,4 @@ | |
| "hono": ">=4.0.0" | ||
| }, | ||
| "packageManager": "pnpm@10.15.0" | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -142,4 +142,4 @@ | |
| "react-dom": ">=19.0.0" | ||
| }, | ||
| "packageManager": "pnpm@10.15.0" | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -114,4 +114,4 @@ | |
| "react-router": ">=7.0.0" | ||
| }, | ||
| "packageManager": "pnpm@10.15.0" | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -126,4 +126,4 @@ | |
| "@types/react": ">=19.0.0" | ||
| }, | ||
| "packageManager": "pnpm@10.15.0" | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟠 Major
🧩 Analysis chain
🌐 Web query:
Microsoft Entra ID OIDC scopes openid profile email read:user user:email💡 Result:
In Microsoft Entra ID (Microsoft identity platform), OpenID Connect (OIDC) scopes are used to request specific information about the authenticated user and to authorize the application [1][2]. The scopes you mentioned are categorized as follows: 1. OIDC Standard Scopes (Supported): - openid: This is a required scope for OpenID Connect flows [1][3]. It instructs the identity platform to return an ID token and translates to the "Sign you in" permission in the consent user interface [1][3][4]. - profile: This scope provides access to a broad range of user information, such as given name, surname, preferred username, and object ID [1]. It is required for accessing claims like given_name, family_name, and preferred_username [5]. - email: This scope enables the application to receive the user's email address claim [6]. For managed users within a tenant, requesting this scope is the standard way to ensure the email claim is included [5]. 2. Other Scopes: - read:user and user:email: These are not standard OIDC scopes supported by the Microsoft identity platform [1][4]. While you may encounter these in other identity providers (such as GitHub), in the Microsoft ecosystem, you should use the official scopes listed above (openid, profile, email) [1][4] or Microsoft Graph permissions (such as User.Read) to access specific user data [7][6][8]. Key Implementation Notes: - UserInfo Endpoint: The openid, profile, and email scopes are used to authorize calls to the Microsoft Graph-hosted UserInfo endpoint, which returns claims about the authenticated user [3][6]. - Automatic Scopes: Be aware that Microsoft Entra ID may automatically include additional scopes, such as offline_access (for refresh tokens) or User.Read, in your authorization requests to ensure proper application functionality [9][2][8]. - Authorization vs. Identity: If your application needs to query data from Microsoft Graph APIs beyond basic identity information, you must request specific Microsoft Graph delegated permissions (e.g., User.Read, Mail.Read) rather than relying solely on OIDC scopes [7][8].
Citations:
Correct Microsoft Identity scopes in the customization example.
The scope values
read:user user:emailare specific to GitHub’s API and are invalid for Microsoft Identity (Entra ID). Using them will cause aninvalid_scopeerror during authorization.Replace the scope string with standard OpenID Connect scopes (
openid profile email) and add Microsoft Graph delegated permissions (e.g.,User.Read) if specific Graph data access is required.Proposed correction
authorize: { params: { // Override default scopes - scope: "read:user user:email", + scope: "openid profile email", }, },📝 Committable suggestion
🤖 Prompt for AI Agents