FastLinkIt

Microsoft Teams — platform admin setup

Adminorganizerteamadmin7 min read

This page is for the platform administrator running a FastLinkIt instance — the person operating flnk.it or a self-hosted equivalent. End users who just want to use Teams meetings should read Microsoft Teams integration instead.

Before users can connect their Microsoft accounts to your FastLinkIt instance, you must register a multi-tenant application in Microsoft Entra ID (the new name for Azure Active Directory). This is a one-time setup per FastLinkIt deployment.

The end-user OAuth flow uses your app registration as the trust anchor — each user grants delegated permissions to your app, and your app calls Microsoft Graph on their behalf using their per-user tokens.

Step 1 — Register the app in Entra ID

  1. Sign in to the Microsoft Entra admin center with an admin account.
  2. Go to Identity → Applications → App registrations.
  3. Click + New registration.
  4. Fill in:
    • Name: FastLinkIt (or whatever you want — users will see this on the consent screen).
    • Supported account types: choose Accounts in any organizational directory (Any Azure AD directory — Multitenant) and personal Microsoft accounts. Multitenant lets users from any organisation connect with their own work/school account. Personal accounts can sign in to OAuth but cannot use the meeting-creation API — the user-facing Integrations UI surfaces a warning when that happens.
    • Redirect URI: select Web, enter https://your-domain.example/api/teams/callback. For the production flnk.it instance this is https://flnk.it/api/teams/callback. The path is fixed by the TeamsConnectEndpoints.cs route.
  5. Click Register.

Step 2 — Note the Client ID

On the app's Overview page, copy the Application (client) ID. You'll paste it into FastLinkIt config in step 5.

Step 3 — Generate a client secret

  1. Left sidebar → Certificates & secrets.
  2. Tab Client secrets+ New client secret.
  3. Description: FastLinkIt production (or similar).
  4. Expiry: 24 months (or the longest your security policy allows). You'll need to rotate before this expires.
  5. Click Add.
  6. Copy the secret VALUE immediately. Entra ID hides it after you leave this page. The Secret ID is not what you need — copy the Value column.

Step 4 — Add API permissions

  1. Left sidebar → API permissions.
  2. Click + Add a permissionMicrosoft GraphDelegated permissions.
  3. Search and tick all four:
Permission What it does
OnlineMeetings.ReadWrite Create and read Teams meeting objects
Calendars.ReadWrite Create the calendar event with the meeting attached. Required since v2 of the integration moved from /me/onlineMeetings to /me/events
User.Read Sign in and read profile (used to display the connected account email in the Integrations UI)
offline_access Receive a refresh token so the integration keeps working after the access token expires (1 hour)
  1. Click Add permissions.

All four are user-consentable, so each end user grants them at OAuth time without needing admin approval. However, if a user's tenant has Users can consent to apps disabled, the user will see a Need admin approval page. The user can either:

  • Send the consent URL to their tenant admin.
  • Their tenant admin grants tenant-wide consent on their copy of the app. Multitenant apps appear in each consenting tenant's Entra ID portal under Enterprise applications the first time anyone from that tenant signs in.

You as the FastLinkIt platform admin do not need to do anything per-tenant. Each external tenant manages its own consent for your app.

Step 5 — Wire the credentials into FastLinkIt config

Open appsettings.json, appsettings.Production.json, or set environment variables — same key path:

"MicrosoftTeams": {
  "ClientId": "<paste the Application (client) ID from step 2>",
  "ClientSecret": "<paste the secret VALUE from step 3>",
  "RedirectUri": "https://your-domain.example/api/teams/callback",
  "TenantId": "common",
  "Scopes": "OnlineMeetings.ReadWrite Calendars.ReadWrite offline_access User.Read"
}

Important notes:

  • Keep TenantId as common for multi-tenant. Set it to a specific tenant Guid only if you want to lock the integration to one organisation.
  • The Scopes string must exactly match the permissions added in step 4 (space-separated). If you add a permission later, update this value AND existing connected users will need to disconnect+reconnect to grant the new scope. The Integrations UI auto-detects this and shows a Reconnect needed banner.
  • For production, prefer storing ClientSecret in environment variables or a secret store rather than committing it to appsettings.json. ASP.NET config binding picks up MicrosoftTeams__ClientSecret=... env vars natively.

Step 6 — Recycle and verify

  1. Recycle the FastLinkIt app pool.
  2. Sign in to FastLinkIt as any user.
  3. Navigate to Account → Integrations.
  4. The Microsoft Teams card should show a Connect Microsoft Teams button (no OAuth not configured warning).
  5. Click it, sign in, accept consent.
  6. You should land back on the Integrations page with a green Connected badge.
  7. Create a test booking on a Teams planner. Within seconds the meeting should appear on your Outlook calendar and the visitor should receive an Outlook invite.

Rotating the client secret

Client secrets expire (max 24 months). To rotate without downtime:

  1. In Entra ID, generate a NEW client secret with a different description.
  2. Update MicrosoftTeams:ClientSecret in your live config.
  3. Recycle the app pool.
  4. Verify a new connection works.
  5. Delete the OLD secret in Entra ID.

No user reconnect is needed for a secret rotation — existing refresh tokens stay valid. (Adding or removing scopes is the only operation that requires user reconnect.)

What gets stored in FastLinkIt

For each user who connects Teams, FastLinkIt stores one row in MeetingProviderConnections (Identity database) with:

  • The user's FastLinkIt UserId.
  • The Provider name (teams).
  • The OAuth access token, encrypted at rest with IDataProtector using the purpose MeetingProvider.Tokens.v1.
  • The encrypted refresh token (same encryption).
  • Token expiry timestamp.
  • Granted scopes string.
  • The user's Microsoft account email (for display in the Integrations UI).
  • Created / updated / last-used timestamps.

Disconnecting deletes the row. It does not revoke the OAuth grant on Microsoft's side — to fully revoke, the user can also remove FastLinkIt from their Microsoft consented apps page.

Troubleshooting

Users see OAuth isn't configured on this server yet — The MicrosoftTeams:ClientId / ClientSecret config values are missing or empty. Check appsettings*.json and any environment variable overrides. Recycle the app pool after fixing.

OAuth callback fails with redirect_uri_mismatch — The RedirectUri in appsettings.json doesn't exactly match any redirect URI registered on the Entra ID app. Whitespace, trailing slashes, and http vs https all matter. Update Entra ID to match your config (or vice versa) and recycle.

Meetings created but not appearing on calendar — The connection is missing the Calendars.ReadWrite scope. Either you're running an old version of FastLinkIt (pre-/me/events switch) and need to upgrade, or the user connected before the scope was added and needs to disconnect+reconnect. The Integrations UI shows a banner prompting this.

Personal accounts complete OAuth but every booking returns 403 from Graph — Expected. Microsoft restricts /me/events and /me/onlineMeetings to work/school accounts. The user-facing Integrations UI displays a warning when a connected account email is in a personal Microsoft domain. See the user guide for visitor-facing workarounds.

Adding a new permission later — Edit the app's API permissions in Entra ID, update MicrosoftTeams:Scopes in config, recycle. Existing users get a Reconnect needed banner because their stored Scope field doesn't include the new permission. Adding new-scope detection in the UI is a 5-line change in Integrations.razor (HasCalendarScope-style helper).

Rejoining the server...

Rejoin failed... trying again in seconds.

Failed to rejoin.
Please retry or reload the page.

The session has been paused by the server.

Failed to resume the session.
Please retry or reload the page.