diff --git a/docs/06-concepts/11-authentication/04-providers/07-microsoft/01-setup.md b/docs/06-concepts/11-authentication/04-providers/07-microsoft/01-setup.md new file mode 100644 index 00000000..86238ee3 --- /dev/null +++ b/docs/06-concepts/11-authentication/04-providers/07-microsoft/01-setup.md @@ -0,0 +1,315 @@ +# Setup + +To set up **Sign in with Microsoft**, you must create an app registration on [Microsoft Entra ID (formerly Azure AD)](https://portal.azure.com/) and configure your Serverpod application accordingly. + +:::caution +You need to install the auth module before you continue, see [Setup](../../setup). +::: + +## Create your Microsoft Entra ID App + +1. Go to [Microsoft Azure Portal](https://portal.azure.com/) and log in with your Microsoft account. +2. Navigate to **Microsoft Entra ID** from the portal menu. + + ![Register App](/img/authentication/providers/microsoft/1-register-app.png) + +3. Go to **App registrations** and click **New registration**. + + ![New App Registration](/img/authentication/providers/microsoft/2-register-new-app.png) + +4. Fill in the required fields: + - **Name**: Enter a name for your application (e.g., "MyApp Authentication"). + - **Supported account types**: Choose one of the following: + - Single tenant only - Default Directory + - Multiple Entra ID tenants + - Any Entra ID Tenant + Personal Microsoft accounts + - Personal accounts only + - **Redirect URI (optional)**: Leave this blank for now. We'll add platform-specific URIs later. + + ![Microsoft App Setup](/img/authentication/providers/microsoft/3-ms.png) + +5. Click **Register** to create your app. + +## Get app credentials + +### Get the client ID + +After registration, you'll be redirected to the app overview page where you can find your **Application (client) ID**. Copy this value - you'll need it for server configuration. + +![Client ID](/img/authentication/providers/microsoft/4-client-id.png) + +### Create a client secret + +1. In your app's menu, navigate to **Certificates & secrets**. +2. Under **Client secrets**, click **New client secret**. + + ![Create Client Secret](/img/authentication/providers/microsoft/5-create-client-secret.png) + +3. Add a description (e.g., "Serverpod Authentication") and choose an expiration period. +4. Click **Add** and immediately copy the **Value** (not the Secret ID). This is your **Client Secret**. + +:::warning +The client secret value is only shown once. Store it securely immediately after creation. Never commit this value to version control. +::: + +### Get the tenant ID (Optional) + +If you're restricting authentication to a specific tenant, you'll need your **Directory (tenant) ID**, which is also shown on the app overview page. For most applications, you can use one of these common values: + +- `common`: Allows both personal Microsoft accounts and work/school accounts (default). +- `organizations`: Allows only work/school accounts. +- `consumers`: Allows only personal Microsoft accounts. + +### Configure redirect URIs + +You need to configure redirect URIs for each platform you want to support. + +1. In your app's menu, navigate to **Authentication**. +2. Click **Add a platform** to configure platform-specific settings. + + ![Authentication Redirect](/img/authentication/providers/microsoft/6-authentication-redirect-uri.png) + +#### Web + +1. Select **Web** as the platform. +2. Add your redirect URI, typically in the format: `https://yourdomain.com/auth.html` + + ![Web Redirect URI](/img/authentication/providers/microsoft/7-web-redirect-uri.png) + +3. Click **Configure**. + +#### iOS and macOS + +1. Select **iOS / macOS** as the platform. +2. For the Bundle ID, enter your app's bundle identifier (e.g., `com.yourcompany.yourapp`). + + ![iOS/macOS Redirect URI](/img/authentication/providers/microsoft/8-ios-macos-redirect-uri.png) + +3. Click **Configure**. + +:::note +For iOS/macOS, Microsoft will automatically generate the redirect URI based on your bundle ID. You can also add custom redirect URIs as needed. +::: + +#### Android + +1. Select **Android** as the platform. +2. Enter your Package name (e.g., `com.yourcompany.yourapp`). +3. Enter your Signature hash. You can get this by running: + + ```bash + keytool -exportcert -alias SIGNATURE_ALIAS -keystore PATH_TO_KEYSTORE | openssl sha1 -binary | openssl base64 + ``` + + ![Android Redirect URI](/img/authentication/providers/microsoft/9-android-redirect-uri.png) + +4. Click **Configure**. + +:::tip +For development, use your debug keystore signature hash. For production builds, use your release keystore signature hash. You can add both to Microsoft Entra ID during setup. +::: + +## Server-side configuration + +### Store the credentials + +Add your Microsoft credentials to the `config/passwords.yaml` file, or set them as environment variables `SERVERPOD_PASSWORD_microsoftClientId`, `SERVERPOD_PASSWORD_microsoftClientSecret`, and `SERVERPOD_PASSWORD_microsoftTenant`. + +```yaml +development: + microsoftClientId: 'YOUR_MICROSOFT_CLIENT_ID' + microsoftClientSecret: 'YOUR_MICROSOFT_CLIENT_SECRET' + microsoftTenant: 'common' # or 'organizations', 'consumers', or your specific tenant ID +``` + +:::warning +Keep your Client Secret confidential. Never commit this value to version control. Store it securely using environment variables or secret management. +::: + +### Configure the Microsoft Identity Provider + +In your main `server.dart` file, configure the Microsoft identity provider: + +```dart +import 'package:serverpod/serverpod.dart'; +import 'package:serverpod_auth_idp_server/core.dart'; +import 'package:serverpod_auth_idp_server/providers/microsoft.dart'; + +void run(List args) async { + final pod = Serverpod( + args, + Protocol(), + Endpoints(), + ); + + pod.initializeAuthServices( + tokenManagerBuilders: [ + JwtConfigFromPasswords(), + ], + identityProviderBuilders: [ + MicrosoftIdpConfig( + clientId: pod.getPassword('microsoftClientId')!, + clientSecret: pod.getPassword('microsoftClientSecret')!, + tenant: pod.getPassword('microsoftTenant') ?? 'common', + ), + ], + ); + + await pod.start(); +} +``` + +:::tip +You can use `MicrosoftIdpConfigFromPasswords()` to automatically load credentials from `config/passwords.yaml` or the `SERVERPOD_PASSWORD_microsoftClientId`, `SERVERPOD_PASSWORD_microsoftClientSecret`, and `SERVERPOD_PASSWORD_microsoftTenant` environment variables: + +```dart +identityProviderBuilders: [ + MicrosoftIdpConfigFromPasswords(), +], +``` + +::: + +### Expose the endpoint + +Create an endpoint that extends `MicrosoftIdpBaseEndpoint` to expose the Microsoft authentication API: + +```dart +import 'package:serverpod_auth_idp_server/providers/microsoft.dart'; + +class MicrosoftIdpEndpoint extends MicrosoftIdpBaseEndpoint {} +``` + +### Generate and migrate + +Finally, run `serverpod generate` to generate the client code and create a migration to initialize the database for the provider. More detailed instructions can be found in the general [identity providers setup section](../../setup#identity-providers-configuration). + +### Basic configuration options + +- `clientId`: Required. The Application (client) ID of your Microsoft Entra ID app. +- `clientSecret`: Required. The Client Secret generated for your Microsoft Entra ID app. +- `tenant`: Optional. Defaults to `'common'`. Can be `'common'`, `'organizations'`, `'consumers'`, or a specific tenant ID. + +For more details on configuration options, see the [configuration section](./configuration). + +## Client-side configuration + +Add the `serverpod_auth_idp_flutter` package to your Flutter app. The Microsoft provider uses [`flutter_web_auth_2`](https://pub.dev/packages/flutter_web_auth_2) to handle the OAuth2 flow, so any documentation there should also apply to this setup. + +### iOS and macOS + +There is no special configuration needed for iOS and macOS for "normal" authentication flows. +However, if you are using **Universal Links** on iOS, they require redirect URIs to use **https**. +Follow the instructions in the [flutter_web_auth_2](https://pub.dev/packages/flutter_web_auth_2#ios) documentation. + +### Android + +In order to capture the callback URL, add the following activity to your `AndroidManifest.xml`. Replace `YOUR_CALLBACK_URL_SCHEME_HERE` and `YOUR_CALLBACK_URL_HOST_HERE` with your actual callback URL scheme and host registered in your Microsoft Entra ID app. + +```xml + + + + + + + + + + + + + + +``` + +### Web + +On the web, you need a specific endpoint to capture the OAuth2 callback. To set this up, create an HTML file (e.g., `auth.html`) inside your project's `./web` folder and add the following content: + +```html + +Authentication complete +

Authentication is complete. If this does not happen automatically, please close the window.

+ +``` + +:::note +You only need a single callback file (e.g. `auth.html`) in your `./web` folder. +This file is shared across all IDPs that use the OAuth2 utility, as long as your redirect URIs point to it. +::: + +## Present the authentication UI + +### Initializing the `MicrosoftSignInService` + +Before presenting any sign-in UI, initialize the Microsoft Sign-In service. This step is necessary to configure the service with your Microsoft app credentials. + +```dart +await client.auth.initializeMicrosoftSignIn( + clientId: 'YOUR_MICROSOFT_CLIENT_ID', + redirectUri: 'YOUR_REGISTERED_REDIRECT_URI', +); +``` + +:::info +For more information on configuration options and environment variables, see the [configuration section](./configuration). +::: + +### Using the `MicrosoftSignInWidget` + +If you have configured the `SignInWidget` as described in the [setup section](../../setup#present-the-authentication-ui), the Microsoft identity provider will be automatically detected and displayed in the sign-in widget. + +You can also use the `MicrosoftSignInWidget` to include the Microsoft authentication flow in your own custom UI. + +```dart +import 'package:serverpod_auth_idp_flutter/serverpod_auth_idp_flutter.dart'; + +MicrosoftSignInWidget( + client: client, + onAuthenticated: () { + // Do something when the user is authenticated. + // + // NOTE: You should not navigate to the home screen here, otherwise + // the user will have to sign in again every time they open the app. + }, + onError: (error) { + // Handle errors + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Error: $error')), + ); + }, +) +``` + +The widget automatically handles: + +- Microsoft Sign-In flow for iOS, Android, Web, and macOS. +- OAuth2 authentication flow. +- Token management. +- Underlying OAuth2 package error handling. + +For details on how to customize the Microsoft Sign-In UI in your Flutter app, see the [customizing the UI section](./customizing-the-ui). diff --git a/docs/06-concepts/11-authentication/04-providers/07-microsoft/02-configuration.md b/docs/06-concepts/11-authentication/04-providers/07-microsoft/02-configuration.md new file mode 100644 index 00000000..0d35fee7 --- /dev/null +++ b/docs/06-concepts/11-authentication/04-providers/07-microsoft/02-configuration.md @@ -0,0 +1,173 @@ +# Configuration + +This page covers configuration options for the Microsoft identity provider beyond the basic setup. + +## Configuration options + +Below is a non-exhaustive list of some of the most common configuration options. For more details on all options, check the `MicrosoftIdpConfig` in-code documentation. + +### Tenant configuration + +The `tenant` parameter determines which accounts can sign in to your application: + +- `'common'` (default) - Allows both personal Microsoft accounts and work/school accounts. +- `'organizations'` - Allows only work/school accounts (any organization). +- `'consumers'` - Allows only personal Microsoft accounts. +- A specific tenant ID - Restricts to accounts from a specific Microsoft Entra ID tenant. + +```dart +final microsoftIdpConfig = MicrosoftIdpConfig( + clientId: pod.getPassword('microsoftClientId')!, + clientSecret: pod.getPassword('microsoftClientSecret')!, + tenant: 'organizations', // Only allow work/school accounts +); +``` + +:::tip +Use `'common'` for the widest user base. Use a specific tenant ID when building internal applications for a single organization. +::: + +### Custom account validation + +You can customize the validation for Microsoft account details before allowing sign-in. By default, the validation checks that the received account details contain a non-empty userIdentifier. + +```dart +final microsoftIdpConfig = MicrosoftIdpConfigFromPasswords( + // Optional: Custom validation for Microsoft account details + microsoftAccountDetailsValidation: (MicrosoftAccountDetails accountDetails) { + // Throw an exception if account doesn't meet custom requirements + if (accountDetails.userIdentifier.isEmpty) { + throw MicrosoftUserInfoMissingDataException(); + } + // Example: Require email to be present + if (accountDetails.email == null || accountDetails.email!.isEmpty) { + throw MicrosoftUserInfoMissingDataException(); + } + }, +); +``` + +:::note +Users may choose not to share their email or other information during the Microsoft login flow. Adjust your validation function carefully to avoid blocking legitimate users. +::: + +### MicrosoftAccountDetails + +The `microsoftAccountDetailsValidation` callback receives a `MicrosoftAccountDetails` record with the following properties: + +| Property | Type | Description | +| ---------- | ------ | ------------- | +| `userIdentifier` | `String` | The Microsoft user's unique identifier (Object ID) | +| `email` | `String?` | The user's email address (may be null) | +| `name` | `String?` | The user's display name from Microsoft | +| `image` | `Uri?` | URL to the user's profile image | + +Example of accessing these properties: + +```dart +microsoftAccountDetailsValidation: (accountDetails) { + print('Microsoft Object ID: ${accountDetails.userIdentifier}'); + print('Email: ${accountDetails.email}'); + print('Display name: ${accountDetails.name}'); + print('Profile image: ${accountDetails.image}'); + + // Custom validation logic + if (accountDetails.email == null) { + throw MicrosoftUserInfoMissingDataException(); + } +}, +``` + +:::info +The properties available depend on the scopes requested and what the user consented to share. +::: + +### Accessing Microsoft APIs + +The default setup allows access to basic user information, such as `name`, `email`. You may require additional access scopes to access other Microsoft APIs, such as accessing a user's calendar, mail, or OneDrive files. + +The default scopes requested are: + +- `openid`: Required for OpenID Connect authentication. +- `profile`: Access to user's basic profile information. +- `email`: Access to user's email address. +- `offline_access`: Allows refresh tokens for long-lived sessions. +- `https://graph.microsoft.com/User.Read`: Access to user's Microsoft Graph profile. + +To request additional scopes, you will need to: + +- Ensure the required API permissions are configured in your Microsoft Entra ID app registration (navigate to **API permissions** in the [Azure Portal](https://portal.azure.com/)). +- Request access to the scopes when signing in. Do this by setting the `scopes` parameter of the `MicrosoftSignInWidget` or `MicrosoftAuthController`. + +A full list of available scopes and Microsoft Graph API permissions can be found in the [Microsoft Graph permissions reference](https://learn.microsoft.com/en-us/graph/permissions-reference). + +:::info +Adding additional scopes may require admin consent depending on your tenant configuration and the sensitivity of the requested permissions. +::: + +### Accessing Microsoft APIs on the server + +On the server side, you can access Microsoft APIs using the access token. The `getExtraMicrosoftInfoCallback` in `MicrosoftIdpConfig` receives the access token and can be used to call Microsoft Graph APIs: + +```dart +import 'package:http/http.dart' as http; + +final microsoftIdpConfig = MicrosoftIdpConfigFromPasswords( + // Optional: Extract additional info from Microsoft Graph APIs + getExtraMicrosoftInfoCallback: (session, { + required accountDetails, + required accessToken, + required transaction, + }) async { + // Use accessToken to call Microsoft Graph APIs and store additional info + // Example: Access user's calendar + final response = await http.get( + Uri.https('graph.microsoft.com', '/v1.0/me/calendar'), + headers: {'Authorization': 'Bearer $accessToken'}, + ); + // Process response and store additional info in the database + }, +); +``` + +## Configuring client IDs on the app + +### Passing client IDs in code + +You can pass the `clientId`, `redirectUri`, and `tenant` directly when initializing the Microsoft Sign-In service: + +```dart +await client.auth.initializeMicrosoftSignIn( + clientId: 'YOUR_MICROSOFT_CLIENT_ID', + redirectUri: 'yourapp://auth', + tenant: 'common', // Optional, defaults to 'common' +); +``` + +This approach is useful when you need different client IDs per platform and want to manage them in your Dart code. + +### Using environment variables + +Alternatively, you can pass client configuration during build time using the `--dart-define` option. The Microsoft Sign-In provider supports the following environment variables: + +- `MICROSOFT_CLIENT_ID`: Your Microsoft Application (client) ID +- `MICROSOFT_REDIRECT_URI`: The callback URI + +**Example usage:** + +```bash +flutter run -d \ + --dart-define="MICROSOFT_CLIENT_ID=your_client_id" \ + --dart-define="MICROSOFT_REDIRECT_URI=msauth://auth" \ + --dart-define="MICROSOFT_TENANT=common" +``` + +This approach is useful when you need to: + +- Manage separate client IDs for different platforms (Android, iOS, Web, macOS) in a centralized way +- Avoid committing client IDs to version control +- Configure different credentials for different build environments (development, staging, production) + +:::tip +You can also set these environment variables in your IDE's run configuration or CI/CD pipeline to avoid passing them manually each time. +::: diff --git a/docs/06-concepts/11-authentication/04-providers/07-microsoft/03-customizing-the-ui.md b/docs/06-concepts/11-authentication/04-providers/07-microsoft/03-customizing-the-ui.md new file mode 100644 index 00000000..0b682896 --- /dev/null +++ b/docs/06-concepts/11-authentication/04-providers/07-microsoft/03-customizing-the-ui.md @@ -0,0 +1,122 @@ +# Customizing the UI + +When using the Microsoft identity provider, you can customize the UI to your liking. You can use the `MicrosoftSignInWidget` to display the Microsoft Sign-In flow in your own custom UI, or you can use the `MicrosoftAuthController` to build a completely custom authentication interface. + +:::info +The `SignInWidget` uses the `MicrosoftSignInWidget` internally to display the Microsoft Sign-In flow. You can also supply a custom `MicrosoftSignInWidget` to the `SignInWidget` to override the default behavior. + +```dart +SignInWidget( + client: client, + microsoftSignInWidget: MicrosoftSignInWidget( + client: client, + // Customize the widget + style: MicrosoftButtonStyle.dark, + ), +) +``` + +::: + +## Using the `MicrosoftSignInWidget` + +The `MicrosoftSignInWidget` handles the complete Microsoft Sign-In flow for your Flutter app. + +You can customize the widget's appearance and behavior: + +```dart +MicrosoftSignInWidget( + client: client, + // Button customization + text: MicrosoftButtonText.continueWith, // or signIn, signUp + type: MicrosoftButtonType.standard, // or icon + style: MicrosoftButtonStyle.light, // or dark + size: MicrosoftButtonSize.large, // or medium + shape: MicrosoftButtonShape.pill, // or rectangular, rounded + logoAlignment: MicrosoftButtonLogoAlignment.left, // or center + minimumWidth: 240, // or null for automatic width + + // Scopes to request from Microsoft + // These are the default scopes. + scopes: const [ + 'openid', + 'profile', + 'email', + 'offline_access', + 'https://graph.microsoft.com/User.Read', + ], + + onAuthenticated: () { + // Do something when the user is authenticated. + // + // NOTE: You should not navigate to the home screen here, otherwise + // the user will have to sign in again every time they open the app. + }, + onError: (error) { + // Handle errors + }, +) +``` + +## Building a custom UI with the `MicrosoftAuthController` + +For more control over the UI, you can use the `MicrosoftAuthController` class, which provides all the authentication logic without any UI components. This allows you to build a completely custom authentication interface. + +```dart +import 'package:serverpod_auth_idp_flutter/serverpod_auth_idp_flutter.dart'; + +final controller = MicrosoftAuthController( + client: client, + onAuthenticated: () { + // Do something when the user is authenticated. + // + // NOTE: You should not navigate to the home screen here, otherwise + // the user will have to sign in again every time they open the app. + }, + onError: (error) { + // Handle errors + }, + scopes: const [ + 'openid', + 'profile', + 'email', + 'offline_access', + 'https://graph.microsoft.com/User.Read', + ], +); + +// Initiate sign-in +await controller.signIn(); +``` + +### MicrosoftAuthController state management + +Your widget should render the appropriate UI based on the `state` property of the controller. You can also use the below state properties to build your UI: + +```dart +// Check current state +final state = controller.state; // MicrosoftAuthState enum + +// Check if loading +final isLoading = controller.isLoading; + +// Check if authenticated +final isAuthenticated = controller.isAuthenticated; + +// Get error message +final errorMessage = controller.errorMessage; + +// Listen to state changes +controller.addListener(() { + setState(() { + // Rebuild UI when controller state changes + }); +}); +``` + +#### MicrosoftAuthController states + +- `MicrosoftAuthState.idle` - Ready for user interaction. +- `MicrosoftAuthState.loading` - Processing a sign-in request. +- `MicrosoftAuthState.error` - An error occurred. +- `MicrosoftAuthState.authenticated` - Authentication was successful. diff --git a/docs/06-concepts/11-authentication/04-providers/07-microsoft/_category_.json b/docs/06-concepts/11-authentication/04-providers/07-microsoft/_category_.json new file mode 100644 index 00000000..93721e04 --- /dev/null +++ b/docs/06-concepts/11-authentication/04-providers/07-microsoft/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Microsoft", + "collapsed": true +} \ No newline at end of file diff --git a/docs/06-concepts/11-authentication/04-providers/10-custom-providers/02-oauth2-utility/01-setup.md b/docs/06-concepts/11-authentication/04-providers/10-custom-providers/02-oauth2-utility/01-setup.md index bec15e00..f4311095 100644 --- a/docs/06-concepts/11-authentication/04-providers/10-custom-providers/02-oauth2-utility/01-setup.md +++ b/docs/06-concepts/11-authentication/04-providers/10-custom-providers/02-oauth2-utility/01-setup.md @@ -92,7 +92,7 @@ Using the previously created `config` object, create the `OAuth2PkceUtil` on you import 'package:serverpod/serverpod.dart'; import 'package:serverpod_auth_idp_server/core.dart'; -class MyProviderIdpEndpoint extends Endpoint { +class MyProviderIdpEndpoint extends IdpBaseEndpoint { final oauth2Util = OAuth2PkceUtil(config: config); Future authenticate( diff --git a/docs/06-concepts/11-authentication/04-providers/10-custom-providers/02-oauth2-utility/02-creating-an-oauth2-based-identity-provider.md b/docs/06-concepts/11-authentication/04-providers/10-custom-providers/02-oauth2-utility/02-creating-an-oauth2-based-identity-provider.md index dce1b64d..64b08132 100644 --- a/docs/06-concepts/11-authentication/04-providers/10-custom-providers/02-oauth2-utility/02-creating-an-oauth2-based-identity-provider.md +++ b/docs/06-concepts/11-authentication/04-providers/10-custom-providers/02-oauth2-utility/02-creating-an-oauth2-based-identity-provider.md @@ -319,7 +319,7 @@ import 'package:serverpod_auth_idp_server/core.dart'; import 'my_provider_idp.dart'; -class MyProviderIdpEndpoint extends Endpoint { +class MyProviderIdpEndpoint extends IdpBaseEndpoint { MyProviderIdp get myProviderIdp => AuthServices.getIdentityProvider(); diff --git a/static/img/authentication/providers/microsoft/1-register-app.png b/static/img/authentication/providers/microsoft/1-register-app.png new file mode 100644 index 00000000..93386c80 Binary files /dev/null and b/static/img/authentication/providers/microsoft/1-register-app.png differ diff --git a/static/img/authentication/providers/microsoft/2-register-new-app.png b/static/img/authentication/providers/microsoft/2-register-new-app.png new file mode 100644 index 00000000..5b510045 Binary files /dev/null and b/static/img/authentication/providers/microsoft/2-register-new-app.png differ diff --git a/static/img/authentication/providers/microsoft/3-ms.png b/static/img/authentication/providers/microsoft/3-ms.png new file mode 100644 index 00000000..5dcbb98e Binary files /dev/null and b/static/img/authentication/providers/microsoft/3-ms.png differ diff --git a/static/img/authentication/providers/microsoft/4-client-id.png b/static/img/authentication/providers/microsoft/4-client-id.png new file mode 100644 index 00000000..e5c0f83a Binary files /dev/null and b/static/img/authentication/providers/microsoft/4-client-id.png differ diff --git a/static/img/authentication/providers/microsoft/5-create-client-secret.png b/static/img/authentication/providers/microsoft/5-create-client-secret.png new file mode 100644 index 00000000..4f39232d Binary files /dev/null and b/static/img/authentication/providers/microsoft/5-create-client-secret.png differ diff --git a/static/img/authentication/providers/microsoft/6-authentication-redirect-uri.png b/static/img/authentication/providers/microsoft/6-authentication-redirect-uri.png new file mode 100644 index 00000000..38f7d517 Binary files /dev/null and b/static/img/authentication/providers/microsoft/6-authentication-redirect-uri.png differ diff --git a/static/img/authentication/providers/microsoft/7-web-redirect-uri.png b/static/img/authentication/providers/microsoft/7-web-redirect-uri.png new file mode 100644 index 00000000..08f8f40c Binary files /dev/null and b/static/img/authentication/providers/microsoft/7-web-redirect-uri.png differ diff --git a/static/img/authentication/providers/microsoft/8-ios-macos-redirect-uri.png b/static/img/authentication/providers/microsoft/8-ios-macos-redirect-uri.png new file mode 100644 index 00000000..f80a375d Binary files /dev/null and b/static/img/authentication/providers/microsoft/8-ios-macos-redirect-uri.png differ diff --git a/static/img/authentication/providers/microsoft/9-android-redirect-uri.png b/static/img/authentication/providers/microsoft/9-android-redirect-uri.png new file mode 100644 index 00000000..9be0e6da Binary files /dev/null and b/static/img/authentication/providers/microsoft/9-android-redirect-uri.png differ