Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 8 additions & 9 deletions .agent-guidance/features/teams-integration.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion .agent-guidance/features/web-dashboard.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 9 additions & 9 deletions apps/docs/providers/communications/microsoft-teams.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@ ROOMOTE_AUTH_MICROSOFT_TENANT_ID=...
Roomote enables Microsoft sign-in only when all three values are present.

For a single-purpose Teams deployment, use the same Entra app for Microsoft
sign-in and the Teams bot. During Roomote setup, Roomote stores hidden Teams bot
credentials copied from the Microsoft client ID, secret, and tenant values. You
can override those bot credentials later from **Settings > Comms** if you need a
separate bot identity.
sign-in and the Teams bot. Roomote setup stores only the Microsoft sign-in
values; at runtime the bot reuses those credentials automatically. You can set
dedicated bot credentials later from **Settings > Comms** if you need a separate
bot identity.

After Microsoft sign-in, **Settings > Linked Accounts** shows the Microsoft
Teams account. Users who signed in with another provider can link Microsoft
Expand All @@ -56,13 +56,13 @@ Set the bot messaging endpoint to:
<public-url>/api/webhooks/teams
```

Enable the Microsoft Teams channel in the bot resource. Roomote setup stores
these bot values automatically from the Microsoft app values. If you configure
with env vars instead, set:
Enable the Microsoft Teams channel in the bot resource. When the Microsoft
sign-in values above are present, Roomote uses them for the bot as well. To use
a separate bot app or configure with dedicated bot env vars instead, set:

```sh
TEAMS_BOT_APP_ID=<microsoft-app-id>
TEAMS_BOT_APP_PASSWORD=<client-secret>
TEAMS_BOT_APP_ID=<bot-app-id>
TEAMS_BOT_APP_PASSWORD=<bot-client-secret>
TEAMS_BOT_TENANT_ID=<tenant-id>
```

Expand Down
79 changes: 9 additions & 70 deletions apps/web/src/app/(onboarding)/setup/ProviderSetupExperience.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,10 @@ type ProviderFieldStatus = ProviderStatus['fields'][number];

const MASKED_VALUE = '••••••••••••••••••••••••••••';

const MICROSOFT_SINGLE_APP_BOT_FIELD_SOURCES: Record<string, string> = {
TEAMS_BOT_APP_ID: 'ROOMOTE_AUTH_MICROSOFT_CLIENT_ID',
TEAMS_BOT_APP_PASSWORD: 'ROOMOTE_AUTH_MICROSOFT_CLIENT_SECRET',
TEAMS_BOT_TENANT_ID: 'ROOMOTE_AUTH_MICROSOFT_TENANT_ID',
};

const MICROSOFT_SETUP_HIDDEN_ENV_VAR_NAMES = new Set([
...Object.keys(MICROSOFT_SINGLE_APP_BOT_FIELD_SOURCES),
'TEAMS_BOT_APP_ID',
'TEAMS_BOT_APP_PASSWORD',
'TEAMS_BOT_TENANT_ID',
'TEAMS_BOT_TOKEN_ENDPOINT',
'TEAMS_BOT_OAUTH_SCOPE',
]);
Expand All @@ -54,80 +50,23 @@ export function getSetupVisibleFields(provider: ProviderStatus | null) {
}

export function getSetupEffectiveFieldValue({
provider,
field,
values,
}: {
provider: ProviderStatus | null;
provider?: ProviderStatus | null;
field: ProviderFieldStatus;
values: Record<string, string>;
}) {
const ownValue = values[field.envVarName] ?? '';

if (ownValue.length > 0) {
return ownValue;
}

if (
provider?.id !== 'microsoft' ||
field.runtimeSatisfied ||
field.savedSatisfied
) {
return '';
}

const sourceEnvVarName =
MICROSOFT_SINGLE_APP_BOT_FIELD_SOURCES[field.envVarName];

return sourceEnvVarName ? (values[sourceEnvVarName] ?? '') : '';
return values[field.envVarName] ?? '';
}

export function getSetupSubmitValues({
provider,
values,
}: {
provider: ProviderStatus | null;
provider?: ProviderStatus | null;
values: Record<string, string>;
}) {
if (!provider) {
return values;
}

const nextValues = { ...values };

for (const field of getSetupVisibleFields(provider)) {
if (nextValues[field.envVarName]?.trim()) {
continue;
}

const copiedValue = getSetupEffectiveFieldValue({
provider,
field,
values,
});

if (copiedValue.trim()) {
nextValues[field.envVarName] = copiedValue;
}
}

if (provider.id === 'microsoft') {
for (const [envVarName, sourceEnvVarName] of Object.entries(
MICROSOFT_SINGLE_APP_BOT_FIELD_SOURCES,
)) {
if (nextValues[envVarName]?.trim()) {
continue;
}

const copiedValue = values[sourceEnvVarName];

if (copiedValue?.trim()) {
nextValues[envVarName] = copiedValue;
}
}
}

return nextValues;
return values;
}

function NumberedStep({
Expand Down Expand Up @@ -374,8 +313,8 @@ function MicrosoftSetupExperience(props: ProviderSetupExperienceProps) {
<NumberedStep number={2}>
<p className="font-semibold">Enter the Microsoft app values.</p>
<p className="text-sm text-muted-foreground max-w-xl">
Roomote stores these values for Microsoft sign-in and also saves
hidden Teams bot credentials copied from them.
Roomote stores these values for Microsoft sign-in. The same app also
powers the Teams bot at runtime.
</p>
<ProviderFields fields={fields} {...props} />
</NumberedStep>
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 13 additions & 11 deletions apps/web/src/app/(onboarding)/setup/StepAuthEnvVars.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -181,31 +181,33 @@ export function StepAuthEnvVars({
typeof window === 'undefined'
? 'https://your-deployment-url'
: window.location.origin;
const teamsBotAppIdField = selectedProvider?.fields.find(
(field) => field.envVarName === 'TEAMS_BOT_APP_ID',
const microsoftClientIdField = selectedProvider?.fields.find(
(field) => field.envVarName === 'ROOMOTE_AUTH_MICROSOFT_CLIENT_ID',
);
const enteredTeamsBotAppId =
isMicrosoftProvider && teamsBotAppIdField
const enteredMicrosoftClientId =
isMicrosoftProvider && microsoftClientIdField
? getSetupEffectiveFieldValue({
provider: selectedProvider,
field: teamsBotAppIdField,
field: microsoftClientIdField,
values,
}).trim()
: '';
const typedTeamsBotAppId = MICROSOFT_APP_ID_PATTERN.test(enteredTeamsBotAppId)
? enteredTeamsBotAppId
const typedMicrosoftClientId = MICROSOFT_APP_ID_PATTERN.test(
enteredMicrosoftClientId,
)
? enteredMicrosoftClientId
: '';
const teamsBotAppIdStored = isMicrosoftProvider
const microsoftClientIdStored = isMicrosoftProvider
? selectedProvider.fields.some(
(field) =>
(field.envVarName === 'TEAMS_BOT_APP_ID' ||
field.envVarName === 'ROOMOTE_AUTH_MICROSOFT_CLIENT_ID') &&
(field.runtimeSatisfied || field.savedSatisfied),
)
: false;
const teamsAppPackageHref = typedTeamsBotAppId
? `/api/setup/teams-app-package?botAppId=${encodeURIComponent(typedTeamsBotAppId)}`
: !bootstrapMode && teamsBotAppIdStored
const teamsAppPackageHref = typedMicrosoftClientId
? `/api/setup/teams-app-package?botAppId=${encodeURIComponent(typedMicrosoftClientId)}`
: !bootstrapMode && microsoftClientIdStored
? '/api/teams/app-package'
: null;
const [showManualSlackValues, setShowManualSlackValues] = useState(false);
Expand Down
9 changes: 8 additions & 1 deletion apps/web/src/trpc/commands/setup-new/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
inArray,
isNull,
sql,
invalidateTeamsBotRuntimeCredentialsCache,
markTaskStartParallelCountEndedAt,
resolveDeploymentEnvVar,
resolveSavedWorkerImage,
Expand Down Expand Up @@ -1583,7 +1584,7 @@ async function saveSetupAuthConfig(input: {
}
const provider = getSetupAuthProvider(input.provider);

return db.transaction(async (tx) => {
const result = await db.transaction(async (tx) => {
const [currentState, persistedEnvVarNames] = await Promise.all([
getPersistedSetupNewState(tx),
getPersistedEnvironmentVariableNames(tx),
Expand Down Expand Up @@ -1655,6 +1656,12 @@ async function saveSetupAuthConfig(input: {
setupNewState,
};
});

if (input.provider === 'microsoft') {
invalidateTeamsBotRuntimeCredentialsCache();
}

return result;
}

export async function saveSetupNewSourceControlProviderChoiceCommand(
Expand Down
43 changes: 18 additions & 25 deletions packages/types/src/setup-auth-config.test.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading