diff --git a/backend/prisma/seed/config.seed.ts b/backend/prisma/seed/config.seed.ts index 6fd5564e1..976635448 100644 --- a/backend/prisma/seed/config.seed.ts +++ b/backend/prisma/seed/config.seed.ts @@ -255,6 +255,18 @@ export const configVariables = { defaultValue: "", obscured: true, }, + "microsoft-usernameClaim": { + type: "string", + defaultValue: "", + }, + "microsoft-roleGeneralAccess": { + type: "string", + defaultValue: "", + }, + "microsoft-roleAdminAccess": { + type: "string", + defaultValue: "", + }, "discord-enabled": { type: "boolean", defaultValue: "false", diff --git a/backend/src/oauth/provider/microsoft.provider.ts b/backend/src/oauth/provider/microsoft.provider.ts index 42262b322..34c97365e 100644 --- a/backend/src/oauth/provider/microsoft.provider.ts +++ b/backend/src/oauth/provider/microsoft.provider.ts @@ -1,9 +1,12 @@ -import { GenericOidcProvider } from "./genericOidc.provider"; +import { GenericOidcProvider, OidcToken } from "./genericOidc.provider"; import { ConfigService } from "../../config/config.service"; import { JwtService } from "@nestjs/jwt"; import { Inject, Injectable } from "@nestjs/common"; import { CACHE_MANAGER } from "@nestjs/cache-manager"; import { Cache } from "cache-manager"; +import { OAuthCallbackDto } from "../dto/oauthCallback.dto"; +import { OAuthSignInDto } from "../dto/oauthSignIn.dto"; +import { OAuthToken } from "./oauthProvider.interface"; @Injectable() export class MicrosoftProvider extends GenericOidcProvider { @@ -26,4 +29,22 @@ export class MicrosoftProvider extends GenericOidcProvider { "oauth.microsoft-tenant", )}/v2.0/.well-known/openid-configuration`; } + + getUserInfo( + token: OAuthToken, + query: OAuthCallbackDto, + _?: string, + ): Promise { + const claim = this.config.get("oauth.microsoft-usernameClaim") || undefined; + const rolePath = "roles"; + const roleGeneralAccess = + this.config.get("oauth.microsoft-roleGeneralAccess") || undefined; + const roleAdminAccess = + this.config.get("oauth.microsoft-roleAdminAccess") || undefined; + return super.getUserInfo(token, query, claim, { + path: rolePath, + generalAccess: roleGeneralAccess, + adminAccess: roleAdminAccess, + }); + } } diff --git a/config.example.yaml b/config.example.yaml index 66f0542c0..5d7f5aab6 100644 --- a/config.example.yaml +++ b/config.example.yaml @@ -159,6 +159,12 @@ oauth: microsoft-clientId: "" #Client secret of the Microsoft OAuth app microsoft-clientSecret: "" + #Username claim in Microsoft token. Leave it blank if you don’t know what this config is. + microsoft-usernameClaim: "" + #Role required for general access. Must be present in a user’s roles for them to log in. Leave it blank if you don't know what this config is. + microsoft-roleGeneralAccess: "" + #Role required for administrative access. Must be present in a user’s roles for them to access the admin panel. Leave it blank if you don't know what this config is. + microsoft-roleAdminAccess: "" #Whether Discord login is enabled discord-enabled: "false" #Limit signing in to users in a specific server. Leave it blank to disable. diff --git a/docs/docs/setup/oauth2login.md b/docs/docs/setup/oauth2login.md index a8eec06fa..ae38d7f85 100644 --- a/docs/docs/setup/oauth2login.md +++ b/docs/docs/setup/oauth2login.md @@ -32,6 +32,32 @@ Please follow the [official guide](https://docs.microsoft.com/en-us/azure/active Redirect URL: `https:///api/oauth/callback/microsoft` +#### Roles configuration + +Roles can be added to EntraID applications to match roles in the app in order to grant admin and/or general access. + +In EntraID, go to the app registration previously created and do the following: + +- Go to "App roles" +- Click "Create app role" +- Enter a display name for the role +- Select "Users/Groups" in "Allowed member types" +- Enter a value (this will be the actual value of the role) +- Add a description +- Enable the role + +Then go to Enterprise applications: + +- Select "Users and groups" +- Click "Add user/group" +- Select the users you want to assign the role to +- Select the role (if there are multiple roles) +- Click "Assign" + +The token sent by EntraID will now include a "roles" field containing the value of the role linked to the user. + +You can now select the desired role for "General Access" and "Admin Access" (using the EntraID role value) in the admin panel. + ### Discord Create an application on [Discord Developer Portal](https://discord.com/developers/applications). diff --git a/frontend/src/i18n/translations/fr-FR.ts b/frontend/src/i18n/translations/fr-FR.ts index cc0fce489..4663fe8c0 100644 --- a/frontend/src/i18n/translations/fr-FR.ts +++ b/frontend/src/i18n/translations/fr-FR.ts @@ -398,6 +398,24 @@ export default { "admin.config.oauth.microsoft-client-id.description": "L’ID du client de l’application Microsoft OAuth", "admin.config.oauth.microsoft-client-secret": "Secret du client Microsoft", "admin.config.oauth.microsoft-client-secret.description": "Le secret du client de l’application Microsoft OAuth", + "admin.config.oauth.microsoft-username-claim": "Revendication du nom d’utilisateur Microsoft", + "admin.config.oauth.microsoft-username-claim.description": "Le champ contenant la revendication du nom d’utilisateur dans le jeton Microsft. Laissez vide si vous ne savez pas quoi indiquer.", + "admin.config.oauth.microsoft-role-general-access.description": "Rôle requis pour un accès général. Doit être présent dans les rôles d'un utilisateur pour qu'il se connecte. " + "Laissez vide si vous ne savez pas ce qu'est cette configuration.", + "admin.config.oauth.microsoft-role-admin-access": "Rôle Microsoft pour l'accès admin", + "admin.config.oauth.microsoft-role-admin-access.description": "Rôle requis pour l'accès administratif. Doit être présent dans les rôles d'un utilisateur pour accéder au panneau d'administration. " + "Laissez vide si vous ne savez pas ce qu'est cette configuration.", + "admin.config.group.general": "Général", + "admin.config.group.general.description": "Configuration générale", + "admin.config.group.github": "GitHub", + "admin.config.group.github.description": "Configuration de l'authentification GitHub", + "admin.config.group.google": "Google", + "admin.config.group.google.description": "Configuration de l'authentification Google", + "admin.config.group.microsoft": "Microsoft", + "admin.config.group.microsoft.description": "Configuration de l'authentification Microsoft", + "admin.config.group.discord": "Discord", + "admin.config.group.discord.description": "Configuration de l'authentification Discord", + "admin.config.group.oidc": "OpenID", + "admin.config.group.oidc.description": "Configuration de l'authentification OpenID", + "admin.config.oauth.microsoft-role-general-access": "Rôle Microsoft pour un accès général", "admin.config.oauth.discord-enabled": "Discord", "admin.config.oauth.discord-enabled.description": "Permettre la connexion via Discord", "admin.config.oauth.discord-limited-users": "Utilisateurs limités sur Discord", diff --git a/frontend/src/pages/admin/config/[category].tsx b/frontend/src/pages/admin/config/[category].tsx index 71c8436d8..dfbb11f0a 100644 --- a/frontend/src/pages/admin/config/[category].tsx +++ b/frontend/src/pages/admin/config/[category].tsx @@ -4,6 +4,7 @@ import { Box, Button, Container, + Divider, Group, Stack, Text, @@ -152,46 +153,193 @@ export default function AppShellDemo() { {t("admin.config.category." + categoryId)} - {configVariables.map((configVariable) => ( - - + {/* General OAuth Settings */} + - - <FormattedMessage - id={`admin.config.${camelToKebab( - configVariable.key, - )}`} - /> - - - - }} - /> - - - - - + {t("admin.config.group.general")} + + } /> + + {t("admin.config.group.general.description")} + + {configVariables + .filter( + (config) => + config.key.startsWith("oauth.") && + ![ + "github", + "google", + "microsoft", + "discord", + "oidc", + ].some((provider) => config.key.includes(provider)), + ) + .map((configVariable) => ( + + + + <FormattedMessage + id={`admin.config.${camelToKebab( + configVariable.key, + )}`} + /> + + + + }} + /> + + + + + + + + ))} - - ))} + + {/* OAuth Providers */} + {["github", "google", "microsoft", "discord", "oidc"].map( + (provider) => ( + + + {provider.charAt(0).toUpperCase() + + provider.slice(1)} + + } + /> + {configVariables + .filter((config) => + config.key.startsWith(`oauth.${provider}`), + ) + .map((configVariable) => ( + + + + <FormattedMessage + id={`admin.config.${camelToKebab( + configVariable.key, + )}`} + /> + + + + }} + /> + + + + + + + + ))} + + ), + )} + + ) : ( + configVariables.map((configVariable) => ( + + + + <FormattedMessage + id={`admin.config.${camelToKebab( + configVariable.key, + )}`} + /> + + + + }} + /> + + + + + + + + )) + )} {categoryId == "general" && ( )}