remix-auth-microsoft
Advanced tools
Comparing version 1.0.3 to 2.0.0
import { StrategyVerifyCallback } from "remix-auth"; | ||
import { OAuth2Profile, OAuth2Strategy, OAuth2StrategyVerifyParams } from "remix-auth-oauth2"; | ||
/** | ||
* @see https://learn.microsoft.com/en-us/azure/active-directory/develop/scopes-oidc#openid-connect-scopes | ||
*/ | ||
export declare type MicrosoftScope = "openid" | "email" | "profile" | "offline_access"; | ||
export interface MicrosoftStrategyOptions { | ||
clientID: string; | ||
clientId: string; | ||
clientSecret: string; | ||
callbackURL: string; | ||
scope?: string; | ||
tenant?: string; | ||
redirectUri: string; | ||
scope?: MicrosoftScope[] | string; | ||
tenantId?: string; | ||
prompt?: string; | ||
@@ -35,2 +39,5 @@ } | ||
} | ||
export declare const MicrosoftStrategyDefaultScopes: MicrosoftScope[]; | ||
export declare const MicrosoftStrategyDefaultName = "microsoft"; | ||
export declare const MicrosoftStrategyScopeSeperator = " "; | ||
export declare class MicrosoftStrategy<User> extends OAuth2Strategy<User, MicrosoftProfile, MicrosoftExtraParams> { | ||
@@ -41,5 +48,6 @@ name: string; | ||
private userInfoURL; | ||
constructor({ clientID, clientSecret, callbackURL, scope, prompt, tenant, }: MicrosoftStrategyOptions, verify: StrategyVerifyCallback<User, OAuth2StrategyVerifyParams<MicrosoftProfile, MicrosoftExtraParams>>); | ||
constructor({ clientId, clientSecret, redirectUri, scope, prompt, tenantId, }: MicrosoftStrategyOptions, verify: StrategyVerifyCallback<User, OAuth2StrategyVerifyParams<MicrosoftProfile, MicrosoftExtraParams>>); | ||
private getScope; | ||
protected authorizationParams(): URLSearchParams; | ||
protected userProfile(accessToken: string): Promise<MicrosoftProfile>; | ||
} |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.MicrosoftStrategy = void 0; | ||
exports.MicrosoftStrategy = exports.MicrosoftStrategyScopeSeperator = exports.MicrosoftStrategyDefaultName = exports.MicrosoftStrategyDefaultScopes = void 0; | ||
const remix_auth_oauth2_1 = require("remix-auth-oauth2"); | ||
exports.MicrosoftStrategyDefaultScopes = [ | ||
"openid", | ||
"profile", | ||
"email", | ||
]; | ||
exports.MicrosoftStrategyDefaultName = "microsoft"; | ||
exports.MicrosoftStrategyScopeSeperator = " "; | ||
class MicrosoftStrategy extends remix_auth_oauth2_1.OAuth2Strategy { | ||
constructor({ clientID, clientSecret, callbackURL, scope, prompt, tenant = "common", }, verify) { | ||
constructor({ clientId, clientSecret, redirectUri, scope, prompt, tenantId = "common", }, verify) { | ||
super({ | ||
clientID, | ||
clientID: clientId, | ||
clientSecret, | ||
callbackURL, | ||
authorizationURL: `https://login.microsoftonline.com/${tenant}/oauth2/v2.0/authorize`, | ||
tokenURL: `https://login.microsoftonline.com/${tenant}/oauth2/v2.0/token`, | ||
callbackURL: redirectUri, | ||
authorizationURL: `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/authorize`, | ||
tokenURL: `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`, | ||
}, verify); | ||
this.name = "microsoft"; | ||
this.name = exports.MicrosoftStrategyDefaultName; | ||
this.userInfoURL = "https://graph.microsoft.com/oidc/userinfo"; | ||
this.scope = scope !== null && scope !== void 0 ? scope : "openid profile email"; | ||
this.scope = this.getScope(scope); | ||
this.prompt = prompt !== null && prompt !== void 0 ? prompt : "none"; | ||
} | ||
//Allow users the option to pass a scope string, or typed array | ||
getScope(scope) { | ||
if (!scope) { | ||
return exports.MicrosoftStrategyDefaultScopes; | ||
} | ||
else if (typeof scope === "string") { | ||
return scope.split(exports.MicrosoftStrategyScopeSeperator); | ||
} | ||
return scope; | ||
} | ||
authorizationParams() { | ||
return new URLSearchParams({ | ||
scope: this.scope, | ||
scope: this.scope.join(exports.MicrosoftStrategyScopeSeperator), | ||
prompt: this.prompt, | ||
@@ -26,3 +43,3 @@ }); | ||
async userProfile(accessToken) { | ||
let response = await fetch(this.userInfoURL, { | ||
const response = await fetch(this.userInfoURL, { | ||
headers: { | ||
@@ -32,5 +49,5 @@ Authorization: `Bearer ${accessToken}`, | ||
}); | ||
let data = await response.json(); | ||
let profile = { | ||
provider: "microsoft", | ||
const data = await response.json(); | ||
const profile = { | ||
provider: exports.MicrosoftStrategyDefaultName, | ||
displayName: data.name, | ||
@@ -37,0 +54,0 @@ id: data.sub, |
{ | ||
"name": "remix-auth-microsoft", | ||
"version": "1.0.3", | ||
"version": "2.0.0", | ||
"main": "./build/index.js", | ||
@@ -5,0 +5,0 @@ "types": "./build/index.d.ts", |
@@ -1,4 +0,5 @@ | ||
# MicrosoftStrategy | ||
# MicrosoftStrategy for [Remix](https://remix.run/) using [Remix-Auth](https://github.com/sergiodxa/remix-auth) | ||
The Microsoft strategy is used to authenticate users against a [Microsoft Identity](https://docs.microsoft.com/en-us/azure/active-directory/develop/) account. This can be a work/school account or a personal Microsoft account, like Skype, Xbox and Outlook.com. It extends the OAuth2Strategy. | ||
The Microsoft strategy is used to authenticate users against an account on [Microsoft Active Directory](https://docs.microsoft.com/en-us/azure/active-directory/develop/) using [Remix-Auth](https://github.com/sergiodxa/remix-auth). | ||
This can be a work/school account or a personal Microsoft account, like Skype, Xbox and Outlook.com. It extends the OAuth2Strategy. | ||
@@ -20,14 +21,28 @@ ## Supported runtimes | ||
Be sure to copy the client secret, Redirect URI and the Application (client) ID (under Overview) because you will need them later. | ||
Change your redirect URI to `https://example.com/auth/microsoft/callback` or `http://localhost:4200/auth/microsoft/callback` if you run it locally. | ||
Be sure to copy the client secret, redirect URI, Tenant ID and the Application (client) ID (under Overview) because you will need them later. | ||
### Install dependencies | ||
```bash | ||
npm install remix-auth-microsoft remix-auth remix-auth-oauth2 | ||
``` | ||
### Create the strategy instance | ||
```ts | ||
// app/services/auth.server.ts | ||
import { MicrosoftStrategy } from "remix-auth-microsoft"; | ||
import { Authenticator } from "remix-auth"; | ||
import { sessionStorage } from "~/services/session.server"; | ||
export let authenticator = new Authenticator<User>(sessionStorage); //User is a custom user types you can define as you want | ||
let microsoftStrategy = new MicrosoftStrategy( | ||
{ | ||
clientID: "YOUR_CLIENT_ID", | ||
clientId: "YOUR_CLIENT_ID", | ||
clientSecret: "YOUR_CLIENT_SECRET", | ||
callbackURL: "https://example.com/auth/microsoft/callback", | ||
redirectUri: "https://example.com/auth/microsoft/callback", | ||
tenantId: "YOUR_TENANT_ID", // optional - necessary for organization without multitenant (see below) | ||
scope: "openid profile email", // optional | ||
@@ -53,2 +68,9 @@ prompt: "login", // optional | ||
### Applications with single-tenant authentication (no multitenant allowed) | ||
If you want to allow login only for users from a single organization, you should add the `tenantId` attribute to the configuration passed to `MicrosoftStrategy`. | ||
The value of `tenantId` should be the **Directory (tenant) ID** found under **Overview** in your App Registration page. | ||
You must also select **Accounts in this organizational directory** as Supported account types in your App Registration. | ||
### Setup your routes | ||
@@ -60,5 +82,5 @@ | ||
return ( | ||
<Form action="/auth/microsoft" method="post"> | ||
<form action="/auth/microsoft" method="post"> | ||
<button>Login with Microsoft</button> | ||
</Form> | ||
</form> | ||
); | ||
@@ -70,8 +92,9 @@ } | ||
// app/routes/auth/microsoft.tsx | ||
import { ActionFunction, LoaderFunction } from "remix"; | ||
import type { ActionArgs } from "@remix-run/node"; | ||
import { authenticator } from "~/auth.server"; | ||
import { redirect } from "@remix-run/node"; | ||
export let loader: LoaderFunction = () => redirect("/login"); | ||
export const loader = () => redirect("/login"); | ||
export let action: ActionFunction = ({ request }) => { | ||
export const action = ({ request }: ActionArgs) => { | ||
return authenticator.authenticate("microsoft", request); | ||
@@ -81,8 +104,8 @@ }; | ||
```tsx | ||
```ts | ||
// app/routes/auth/microsoft/callback.tsx | ||
import { ActionFunction, LoaderFunction } from "remix"; | ||
import type { LoaderArgs } from "@remix-run/node"; | ||
import { authenticator } from "~/auth.server"; | ||
export let loader: LoaderFunction = ({ request }) => { | ||
export const loader = ({ request }: LoaderArgs) => { | ||
return authenticator.authenticate("microsoft", request, { | ||
@@ -95,20 +118,20 @@ successRedirect: "/dashboard", | ||
### Allow login only with accounts from a single organization (tenant) | ||
### Add Session Storage | ||
If you want to allow login only for users from a single organization, you should add the `tenant` attribute to the configuration passed to `MicrosoftStrategy`. The value of `tenant` should be the **Directory (tenant) ID** found under **Overview** in your App Registration page. | ||
```ts | ||
// app/services/session.server.ts | ||
import { createCookieSessionStorage } from "@remix-run/node"; | ||
You must also select **Accounts in this organizational directory** as Supported account types in your App Registration. | ||
export let sessionStorage = createCookieSessionStorage({ | ||
cookie: { | ||
name: "_session", // use any name you want here | ||
sameSite: "lax", // this helps with CSRF | ||
path: "/", // remember to add this so the cookie will work in all routes | ||
httpOnly: true, // for security reasons, make this cookie http only | ||
secrets: ["s3cr3t"], // replace this with an actual secret | ||
secure: process.env.NODE_ENV === "production", // enable this in prod only | ||
}, | ||
}); | ||
```ts | ||
let microsoftStrategy = new MicrosoftStrategy( | ||
{ | ||
clientID: "YOUR_CLIENT_ID", | ||
clientSecret: "YOUR_CLIENT_SECRET", | ||
callbackURL: "https://example.com/auth/microsoft/callback", | ||
tenant: "YOUR_TENANT_ID", | ||
}, | ||
async (accessToken, _, extraParams, profile) => { | ||
return User.findOrCreate({ email: profile.emails[0].value }); | ||
} | ||
); | ||
export let { getSession, commitSession, destroySession } = sessionStorage; | ||
``` |
12439
114
132