From 08547a8768f3a3e64cd520abf94bcca0f2e07764 Mon Sep 17 00:00:00 2001 From: TekH Date: Mon, 17 Aug 2026 15:06:51 +0200 Subject: [PATCH] docs: add full OAuth2 setup guide for Google and Microsoft to README; exclude Google OAuth2 token JSON from version control --- .gitignore | 1 + README.md | 300 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 301 insertions(+) diff --git a/.gitignore b/.gitignore index 29d62c1..d7480a3 100644 --- a/.gitignore +++ b/.gitignore @@ -373,3 +373,4 @@ FodyWeavers.xsd /src/DigitalData.MessagingService.API/appsettings.Secrets.json /src/presentation/DigitalData.MessagingService.API/appsettings.Secrets.json /src/presentation/DigitalData.MessagingService.API/appsettings.Secrets.json +/src/presentation/DigitalData.MessagingService.API/oauth.htek0100@gmail.com.json diff --git a/README.md b/README.md index baa1263..9f0a088 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,302 @@ # DigitalData.MessagingService +A .NET 8 messaging service for sending and receiving emails via SMTP, IMAP, POP3 and OAuth2, with RabbitMQ-based async delivery. + +--- + +## Email Account Configuration + +Each account is configured under `EmailAccounts.Accounts` in `appsettings.Secrets.json`. + +> Different providers require different configuration fields. See provider-specific sections below. + +### Common fields (all providers) + +```json +{ + "Id": 1, + "Username": "user@example.com", + "Password": "your_password", + "SmtpServer": "smtp.example.com", + "SmtpPort": 465, + "SmtpUseSsl": true, + "UseOAuth2": false, + "ImapServer": "imap.example.com", + "ImapPort": 993, + "ImapUseSsl": true, + "Pop3Server": "pop.example.com", + "Pop3Port": 995, + "Pop3UseSsl": true, + "IncomingProtocol": 1 +} +``` + +> `Password` is always retained. When `UseOAuth2 = true`, SMTP/IMAP/POP3 connections use OAuth2 tokens +> instead of the password. When `UseOAuth2 = false`, the password is used directly. +> The `IncomingProtocol` field independently controls which protocol is used for receiving emails. + +#### `IncomingProtocol` values + +| Value | Meaning | +|-------|---------| +| `0` | None — send-only account, skipped by sync worker | +| `1` | IMAP with username/password | +| `2` | POP3 with username/password | +| `3` | IMAP with OAuth2 | +| `4` | POP3 with OAuth2 | + +#### `OAuth2Provider` values + +| Value | Meaning | +|-------|---------| +| `0` | None | +| `1` | Microsoft (Azure AD / Microsoft 365) | +| `2` | Google (Gmail / Google Workspace) | + +--- + +## Provider-specific OAuth2 Configuration + +### Google (Gmail / Google Workspace) + +Google uses **user-delegated OAuth2** (authorization code flow). A one-time interactive authorization +is required to obtain a refresh token. The refresh token is then stored in the database and reused +automatically for all subsequent operations. + +> The refresh token survives application restarts. It will not be overwritten by the seed process +> unless `OAuth2RefreshToken` is explicitly set to a non-empty value in `appsettings.Secrets.json`. + +#### Required fields + +```json +{ + "UseOAuth2": true, + "OAuth2ClientId": "YOUR_CLIENT_ID.apps.googleusercontent.com", + "OAuth2ClientSecret": "GOCSPX-YOUR_CLIENT_SECRET", + "OAuth2RefreshToken": "", + "OAuth2TenantId": "", + "OAuth2Provider": 2 +} +``` + +#### Google Cloud Console setup (one-time) + +1. Go to [console.cloud.google.com](https://console.cloud.google.com) and create or select a project. +2. Enable the **Gmail API** under *APIs & Services ? Library*. +3. Go to *APIs & Services ? Credentials* ? **+ Create Credentials** ? **OAuth 2.0 Client ID**: + - Application type: **Web application** + - **Authorized redirect URIs**: add `https://YOUR_HOST/api/oauth2/google/callback` + (e.g. `https://localhost:7261/api/oauth2/google/callback` for local development) +4. Go to *APIs & Services ? OAuth consent screen*: + - Add the Gmail account under **Test users** (required while app is in Testing mode). + +#### Obtaining the refresh token via the built-in authorization endpoint + +The application provides a built-in OAuth2 flow — no external tools needed. + +1. Open a browser and navigate to: + ``` + GET /api/oauth2/google/authorize/{accountId} + ``` + Example: `https://localhost:7261/api/oauth2/google/authorize/3` + +2. You will be redirected to Google's consent screen. If you see **"Google hasn't verified this app"**, + click **Continue** — this is expected while the app is in Testing mode. + +3. Sign in with the Gmail account and grant access. + +4. Google redirects back to `/api/oauth2/google/callback` automatically. + The application exchanges the authorization code for a refresh token and saves it to the database. + +5. A success response is returned: + ```json + { "success": true, "username": "user@gmail.com", "message": "..." } + ``` + +6. The sync worker and all IMAP/SMTP operations will now work automatically. + +> ?? The refresh token must be re-obtained if `invalid_grant` is returned. +> This happens if the token is unused for 6 months or if the user revokes access. + +--- + +### Microsoft 365 / Exchange Online (Azure AD) + +Microsoft uses **application-level OAuth2** (client credentials flow — no user interaction required). +Tokens are acquired automatically using the client ID, secret and tenant ID. No authorization endpoint +needs to be visited. + +#### Required fields + +```json +{ + "UseOAuth2": true, + "OAuth2ClientId": "YOUR_APP_CLIENT_ID", + "OAuth2ClientSecret": "YOUR_APP_CLIENT_SECRET_VALUE", + "OAuth2TenantId": "yourorg.onmicrosoft.com", + "OAuth2Provider": 1 +} +``` + +#### Azure Portal setup (one-time) + +1. Go to [portal.azure.com](https://portal.azure.com) ? **Azure Active Directory** ? **App registrations** ? **+ New registration**. +2. Go to **Certificates & secrets** ? **+ New client secret** ? copy the **Value** (not the ID). + - Set this as `OAuth2ClientSecret`. +3. Go to **API permissions** ? **+ Add a permission** ? **APIs my organization uses** ? **Office 365 Exchange Online**: + - Add **Application permissions**: `IMAP.AccessAsApp`, `SMTP.SendAsApp`, `POP.AccessAsApp` + - Click **Grant admin consent** +4. In Exchange Online PowerShell, register the service principal for the mailbox: + ```powershell + New-ServicePrincipal -AppId -ServiceId -DisplayName "MessagingService" + Add-MailboxPermission -Identity "user@yourorg.onmicrosoft.com" -User -AccessRights FullAccess + ``` +5. Set `OAuth2TenantId` to the full domain (e.g. `yourorg.onmicrosoft.com`) or tenant GUID. + +> ?? `OAuth2ClientSecret` must be the **Value** shown at secret creation time, not the Secret ID (GUID). +> The value is only visible once — if lost, create a new secret. + +> ?? `OAuth2TenantId` must be a full domain (`yourorg.onmicrosoft.com`), a tenant GUID, +> or `common`. Short names like `yourorg` are not valid and will cause `AADSTS900023`. + +> No browser-based authorization is required for Microsoft — the application acquires tokens +> automatically on first use and caches them in memory until 5 minutes before expiry. + + +--- + +## Email Account Configuration + +Each account is configured under `EmailAccounts.Accounts` in `appsettings.Secrets.json`. + +> Different providers require different configuration fields. See provider-specific sections below. + +### Common fields (all providers) + +```json +{ + "Id": 1, + "Username": "user@example.com", + "Password": "your_password", + "SmtpServer": "smtp.example.com", + "SmtpPort": 465, + "SmtpUseSsl": true, + "UseOAuth2": false, + "ImapServer": "imap.example.com", + "ImapPort": 993, + "ImapUseSsl": true, + "Pop3Server": "pop.example.com", + "Pop3Port": 995, + "Pop3UseSsl": true, + "IncomingProtocol": 1 +} +``` + +> `Password` is always retained. When `UseOAuth2 = true`, SMTP/IMAP/POP3 connections use OAuth2 tokens +> instead of the password. When `UseOAuth2 = false`, the password is used directly. +> The `IncomingProtocol` field independently controls which protocol is used for receiving emails. + +#### `IncomingProtocol` values + +| Value | Meaning | +|-------|---------| +| `0` | None — send-only account, skipped by sync worker | +| `1` | IMAP with username/password | +| `2` | POP3 with username/password | +| `3` | IMAP with OAuth2 | +| `4` | POP3 with OAuth2 | + +#### `OAuth2Provider` values + +| Value | Meaning | +|-------|---------| +| `0` | None | +| `1` | Microsoft (Azure AD / Microsoft 365) | +| `2` | Google (Gmail / Google Workspace) | + +--- + +## Provider-specific OAuth2 Configuration + +### Google (Gmail / Google Workspace) + +Google uses **user-delegated OAuth2** (not client credentials). A one-time authorization flow is required to obtain a refresh token. + +#### Required fields + +```json +{ + "UseOAuth2": true, + "OAuth2ClientId": "YOUR_CLIENT_ID.apps.googleusercontent.com", + "OAuth2ClientSecret": "GOCSPX-YOUR_CLIENT_SECRET", + "OAuth2RefreshToken": "1//04YOUR_REFRESH_TOKEN", + "OAuth2TenantId": "", + "OAuth2Provider": 2 +} +``` + +#### Setup — obtaining the refresh token (one-time) + +1. Go to [console.cloud.google.com](https://console.cloud.google.com) and create or select a project. +2. Enable the **Gmail API** under *APIs & Services ? Library*. +3. Go to *APIs & Services ? Credentials* ? **+ Create Credentials** ? **OAuth 2.0 Client ID**. + - Application type: **Web application** + - Authorized redirect URIs: `https://developers.google.com/oauthplayground` +4. Go to *APIs & Services ? OAuth consent screen*: + - Add the Gmail account under **Test users** (required while app is in Testing mode). +5. Go to [developers.google.com/oauthplayground](https://developers.google.com/oauthplayground): + - Click **?? Settings** ? enable **"Use your own OAuth credentials"** + - Enter your **Client ID** and **Client Secret** (from step 3) + - Close settings +6. In the scope input (Step 1), enter `https://mail.google.com/` ? **Authorize APIs** +7. Sign in with the Gmail account ? grant access +8. Click **Exchange authorization code for tokens** (Step 2) +9. Copy the `refresh_token` value from the response +10. Set `OAuth2RefreshToken` in `appsettings.Secrets.json` + +> ?? The refresh token must be obtained using **your own client credentials** in Playground settings. +> If obtained with Playground's default credentials, it will not work with your client secret. + +> ?? Google refresh tokens expire if unused for 6 months, or if the user revokes access. +> The token must be re-obtained if `invalid_grant` is returned. + +--- + +### Microsoft 365 / Exchange Online (Azure AD) + +Microsoft uses **application-level OAuth2** (client credentials flow — no user interaction required). + +#### Required fields + +```json +{ + "UseOAuth2": true, + "OAuth2ClientId": "YOUR_APP_CLIENT_ID", + "OAuth2ClientSecret": "YOUR_APP_CLIENT_SECRET_VALUE", + "OAuth2TenantId": "yourorg.onmicrosoft.com", + "OAuth2Provider": 1 +} +``` + +#### Setup + +1. Go to [portal.azure.com](https://portal.azure.com) ? **Azure Active Directory** ? **App registrations** ? **+ New registration**. +2. Go to **Certificates & secrets** ? **+ New client secret** ? copy the **Value** (not the ID). + - Set this as `OAuth2ClientSecret`. +3. Go to **API permissions** ? **+ Add a permission** ? **APIs my organization uses** ? **Office 365 Exchange Online**: + - Add **Application permissions**: `IMAP.AccessAsApp`, `SMTP.SendAsApp`, `POP.AccessAsApp` + - Click **Grant admin consent** +4. In Exchange Online PowerShell, register the service principal for the mailbox: + ```powershell + New-ServicePrincipal -AppId -ServiceId -DisplayName "MessagingService" + Add-MailboxPermission -Identity "user@yourorg.onmicrosoft.com" -User -AccessRights FullAccess + ``` +5. Set `OAuth2TenantId` to the full domain (e.g. `yourorg.onmicrosoft.com`) or tenant GUID. + +> ?? `OAuth2ClientSecret` must be the **Value** shown at secret creation time, not the Secret ID (GUID). +> The value is only visible once — if lost, create a new secret. + +> ?? `OAuth2TenantId` must be a full domain (`yourorg.onmicrosoft.com`), a tenant GUID, +> or `common`. Short names like `yourorg` are not valid and will cause `AADSTS900023`. + +