Backend
- Custom Ally driver Microsoft (Oauth2Driver) — Microsoft n'est pas dans
les providers built-in, mais le driver dérive de Oauth2Driver en quelques
lignes. Endpoints v2.0 (Microsoft Identity Platform), Graph /me pour le
profil, fallback userPrincipalName si mail null (comptes perso).
- Tenant configurable via MICROSOFT_TENANT (défaut 'common' — accepte
work/school + perso ; 'organizations' pour M365 strict).
- Migration 1400 : ajout microsoft_id nullable unique sur users.
- AuthMicrosoftController : redirect + callback (même pattern que Google).
- Refacto : extraction d'un service sso_session.ts (findOrCreateUserFromSso,
nextRouteAfterSso, emitSsoSessionAndRedirect) → AuthGoogle + AuthMicrosoft
partagent la logique.
- Routes /api/v1/auth/microsoft/{redirect,callback}.
Frontend
- Composant SsoButton générique (provider="google"|"microsoft") avec logo
officiel inline pour chaque. Remplace l'ancien GoogleButton.
- Login + signup : pile verticale "Continuer avec Google" + "Continuer
avec Microsoft", puis séparateur "ou", puis form email/password.
- Route SPA renommée /auth/google/complete → /auth/sso/complete (partagée
entre les deux providers, la callback API redirige toujours dessus).
- Erreurs SSO sur /login : ?google=... ET ?microsoft=... → toast contextuel.
K3s
- ConfigMap rubis-api-config : ajout MICROSOFT_TENANT + MICROSOFT_CALLBACK_URL.
- Secret rubis-app-secrets : ajout MICROSOFT_CLIENT_ID + MICROSOFT_CLIENT_SECRET.
Doc
- .claude/deploy-memory.md : procédure Azure / Entra ID app registration.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
49 lines
1.8 KiB
TypeScript
49 lines
1.8 KiB
TypeScript
import env from '#start/env'
|
|
import { defineConfig, services } from '@adonisjs/ally'
|
|
import { microsoftService } from '#services/ally/microsoft_driver'
|
|
|
|
/**
|
|
* Configuration des providers OAuth (Ally).
|
|
*
|
|
* V1 : Google + Microsoft 365 (cf. CLAUDE.md → Auth).
|
|
* Le callback URL pointe vers l'API (/api/v1/auth/{provider}/callback).
|
|
* En prod, le reverse proxy nginx (rubis-web) achemine /api/* vers ce
|
|
* service, donc la même URL fonctionne browser et provider.
|
|
*/
|
|
const allyConfig = defineConfig({
|
|
google: services.google({
|
|
clientId: env.get('GOOGLE_CLIENT_ID', ''),
|
|
clientSecret: env.get('GOOGLE_CLIENT_SECRET', ''),
|
|
callbackUrl: env.get(
|
|
'GOOGLE_CALLBACK_URL',
|
|
'http://localhost:3333/api/v1/auth/google/callback'
|
|
),
|
|
// Scopes minimaux : on a juste besoin de l'email + nom + photo (avatar
|
|
// optionnel V2). Pas de Drive/Calendar : on ne touche pas aux données
|
|
// Google de l'utilisateur, on s'en sert juste comme provider d'identité.
|
|
scopes: ['userinfo.email', 'userinfo.profile'],
|
|
prompt: 'select_account',
|
|
}),
|
|
microsoft: microsoftService({
|
|
clientId: env.get('MICROSOFT_CLIENT_ID', ''),
|
|
clientSecret: env.get('MICROSOFT_CLIENT_SECRET', ''),
|
|
callbackUrl: env.get(
|
|
'MICROSOFT_CALLBACK_URL',
|
|
'http://localhost:3333/api/v1/auth/microsoft/callback'
|
|
),
|
|
// tenant=common : accepte work/school (Microsoft 365) ET comptes personnels
|
|
// (Outlook, Hotmail). Pour limiter à M365 strict, mettre 'organizations'.
|
|
tenant: env.get('MICROSOFT_TENANT', 'common'),
|
|
scopes: ['openid', 'profile', 'email', 'User.Read'],
|
|
prompt: 'select_account',
|
|
}),
|
|
})
|
|
|
|
export default allyConfig
|
|
|
|
declare module '@adonisjs/ally/types' {
|
|
interface SocialProviders extends InferSocialProviders<typeof allyConfig> {}
|
|
}
|
|
|
|
import type { InferSocialProviders } from '@adonisjs/ally/types'
|