Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
197 changes: 197 additions & 0 deletions docs/src/content/docs/(core)/oauth/microsoft-identity.mdx
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",
},
Comment on lines +91 to +96

Copy link
Copy Markdown

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:email are specific to GitHub’s API and are invalid for Microsoft Identity (Entra ID). Using them will cause an invalid_scope error 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

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
microsoftIdentity({
authorize: {
params: {
// Override default scopes
scope: "read:user user:email",
},
microsoftIdentity({
authorize: {
params: {
// Override default scopes
scope: "openid profile email",
},
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/src/content/docs/`(core)/oauth/microsoft-identity.mdx around lines 91 -
96, The Microsoft Identity customization example is using GitHub-only scopes, so
update the scope value in the microsoftIdentity authorize params example to
standard OpenID Connect scopes and, if needed, mention Microsoft Graph delegated
permissions for extra API access. Make sure the example under microsoftIdentity
uses valid Entra ID scopes rather than read:user and user:email so the
customization snippet matches the intended provider behavior.

},
}),
],
})

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)
62 changes: 62 additions & 0 deletions packages/core/src/oauth/microsoft-identity.ts
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

Copy link
Copy Markdown

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

🏁 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 :tenantId placeholder in the Microsoft Identity issuer.

The MICROSOFT_IDENTITY_ISSUER constant contains a literal :tenantId string that is never interpolated or replaced by the core framework. Microsoft's v2.0 OpenID Connect endpoints strictly require a valid tenant segment (e.g., common, organizations, consumers, or a specific GUID) to resolve the discovery document. Verification confirms no codebase logic performs string replacement on this placeholder, meaning the provider will attempt to fetch metadata from an invalid path (.../:tenantId/v2.0), causing authentication failure.

Update the microsoftIdentity factory to accept a tenantId parameter (defaulting to "common") and construct the issuer dynamically:

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 docs/src/content/docs/(core)/oauth/microsoft-identity.mdx to reflect this new configuration requirement.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/core/src/oauth/microsoft-identity.ts` around lines 34 - 52, The
Microsoft Identity issuer still contains an unresolved :tenantId placeholder, so
the provider builds an invalid discovery URL. Update the microsoftIdentity
factory to accept a tenantId option (defaulting to common) and construct the
issuer dynamically instead of relying on MICROSOFT_IDENTITY_ISSUER as a fixed
string. Make sure the returned OpenIDProvider uses the computed issuer in
microsoftIdentity, and update the Microsoft Identity docs to describe the
required tenant configuration and valid tenant values.

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>
}
2 changes: 1 addition & 1 deletion packages/elysia/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,4 +104,4 @@
"elysia": ">=1.0.0"
},
"packageManager": "pnpm@10.15.0"
}
}
2 changes: 1 addition & 1 deletion packages/express/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -109,4 +109,4 @@
"express": ">=4.0.0"
},
"packageManager": "pnpm@10.15.0"
}
}
2 changes: 1 addition & 1 deletion packages/hono/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -107,4 +107,4 @@
"hono": ">=4.0.0"
},
"packageManager": "pnpm@10.15.0"
}
}
2 changes: 1 addition & 1 deletion packages/next/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -142,4 +142,4 @@
"react-dom": ">=19.0.0"
},
"packageManager": "pnpm@10.15.0"
}
}
2 changes: 1 addition & 1 deletion packages/react-router/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -114,4 +114,4 @@
"react-router": ">=7.0.0"
},
"packageManager": "pnpm@10.15.0"
}
}
2 changes: 1 addition & 1 deletion packages/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -126,4 +126,4 @@
"@types/react": ">=19.0.0"
},
"packageManager": "pnpm@10.15.0"
}
}
Loading