-
Notifications
You must be signed in to change notification settings - Fork 4.3k
feat(js,react,api-service): implement MS Teams connect and link-user components #10870
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
17 commits
Select commit
Hold shift + click to select a range
c22f60d
feat(api, dashboard, ui): enhance MS Teams integration with user link…
djabarovgeorge 450fa38
feat(api, worker): integrate MsTeamsTokenService for improved MS Team…
djabarovgeorge 23b699b
refactor(worker): remove debug logging from send-message use case
djabarovgeorge 45851b4
enhance(MsTeamsOauthCallback): refine app ID resolution with distribu…
djabarovgeorge 5f749df
refactor(TeamsSetupGuide): update OAuth callback URL handling and enh…
djabarovgeorge b9e6ab9
fix(MsTeamsOAuth): improve error handling and HTML response for missi…
djabarovgeorge ed1fd53
fix(MsTeamsLinkUser): improve OAuth URL handling and popup management
djabarovgeorge 2b4ea79
fix(MsTeamsLinkUser): streamline OAuth URL handling by removing popup…
djabarovgeorge ad188d4
feat(MsTeamsOAuth): add autoLinkUser feature for streamlined user lin…
djabarovgeorge 408dd08
feat(OAuth): enhance autoLinkUser functionality across integrations
djabarovgeorge e40487f
feat(OAuth): introduce new endpoints for connect and link user OAuth …
djabarovgeorge bea36c4
test(OAuth): improve error handling in MsTeams OAuth URL generation t…
djabarovgeorge 70917a0
test(MsTeamsOauthCallback): enhance error handling in OAuth callback …
djabarovgeorge 705a1b9
test(MsTeamsOauthCallback): refactor tests to improve response valida…
djabarovgeorge 6af03dc
refactor(MsTeamsConnectButton & SlackConnectButton): streamline integ…
djabarovgeorge b00843f
chore: update playground
djabarovgeorge d5571cf
fix(OAuth): update OAuth URL generation to use dynamic base URL
djabarovgeorge File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
83 changes: 83 additions & 0 deletions
83
apps/api/src/app/integrations/dtos/generate-connect-oauth-url-request.dto.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,83 @@ | ||
| import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; | ||
| import { ApiContextPayload, IsValidContextPayload } from '@novu/application-generic'; | ||
| import { ConnectionMode, ContextPayload } from '@novu/shared'; | ||
| import { IsArray, IsBoolean, IsDefined, IsIn, IsNotEmpty, IsOptional, IsString } from 'class-validator'; | ||
| import { SLACK_DEFAULT_OAUTH_SCOPES } from '../usecases/generate-chat-oath-url/generate-slack-oath-url/generate-slack-oauth-url.usecase'; | ||
|
|
||
| export class GenerateConnectOauthUrlRequestDto { | ||
| @ApiPropertyOptional({ | ||
| type: String, | ||
| description: | ||
| 'The subscriber ID to associate with the channel connection. ' + | ||
| 'For Slack: optional for workspace connections (required only for incoming-webhook scope). ' + | ||
| 'For MS Teams: optional. Admin consent is tenant-wide.', | ||
| example: 'subscriber-123', | ||
| }) | ||
| @IsOptional() | ||
| @IsString() | ||
| subscriberId?: string; | ||
|
|
||
| @ApiProperty({ | ||
| type: String, | ||
| description: 'Integration identifier', | ||
| }) | ||
| @IsString() | ||
| @IsDefined() | ||
| @IsNotEmpty({ message: 'Integration identifier is required' }) | ||
| integrationIdentifier: string; | ||
|
|
||
| @ApiPropertyOptional({ | ||
| type: String, | ||
| description: 'Identifier of the channel connection that will be created. Generated automatically if not provided.', | ||
| example: 'slack-connection-abc123', | ||
| }) | ||
| @IsString() | ||
| @IsOptional() | ||
| connectionIdentifier?: string; | ||
|
|
||
| @ApiContextPayload() | ||
| @IsOptional() | ||
| @IsValidContextPayload({ maxCount: 5 }) | ||
| context?: ContextPayload; | ||
|
|
||
| @ApiPropertyOptional({ | ||
| type: [String], | ||
| description: | ||
| `**Slack only**: OAuth scopes to request during authorization. ` + | ||
| `If not specified, default scopes will be used: ${SLACK_DEFAULT_OAUTH_SCOPES.join(', ')}. ` + | ||
| `**MS Teams**: ignored — uses admin consent with pre-configured Azure AD permissions.`, | ||
| example: ['chat:write', 'chat:write.public', 'channels:read'], | ||
| }) | ||
| @IsOptional() | ||
| @IsArray() | ||
| @IsString({ each: true }) | ||
| scope?: string[]; | ||
|
|
||
| @ApiPropertyOptional({ | ||
| type: String, | ||
| description: | ||
| 'Connection mode that determines how the channel connection is scoped. ' + | ||
| '"subscriber" (default) associates the connection with a specific subscriber. ' + | ||
| '"shared" associates the connection with a context instead of a subscriber.', | ||
| enum: ['subscriber', 'shared'], | ||
| example: 'shared', | ||
| }) | ||
| @IsOptional() | ||
| @IsString() | ||
| @IsIn(['subscriber', 'shared']) | ||
| connectionMode?: ConnectionMode; | ||
|
|
||
| @ApiPropertyOptional({ | ||
| type: Boolean, | ||
| description: | ||
| 'When true (default when connectionMode is "subscriber"), after the workspace/tenant connection is created ' + | ||
| 'the OAuth flow also links the subscriber who clicked "Connect" as a personal endpoint. ' + | ||
| 'For Slack, uses the authed_user.id returned by oauth.v2.access — no extra redirect. ' + | ||
| 'For MS Teams, triggers a second OAuth redirect for delegated user-identity consent. ' + | ||
| 'Set to false to only create the workspace connection without linking the individual user.', | ||
| example: true, | ||
| }) | ||
| @IsOptional() | ||
| @IsBoolean() | ||
| autoLinkUser?: boolean; | ||
| } |
57 changes: 57 additions & 0 deletions
57
apps/api/src/app/integrations/dtos/generate-link-user-oauth-url-request.dto.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; | ||
| import { ApiContextPayload, IsValidContextPayload } from '@novu/application-generic'; | ||
| import { ContextPayload } from '@novu/shared'; | ||
| import { IsArray, IsDefined, IsNotEmpty, IsOptional, IsString } from 'class-validator'; | ||
| import { SLACK_LINK_USER_OAUTH_SCOPES } from '../usecases/generate-chat-oath-url/generate-slack-oath-url/generate-slack-oauth-url.usecase'; | ||
|
|
||
| export class GenerateLinkUserOauthUrlRequestDto { | ||
| @ApiProperty({ | ||
| type: String, | ||
| description: | ||
| 'The subscriber ID to link to their chat identity. Required — this operation always binds ' + | ||
| 'a specific subscriber to a user identity in the chat provider.', | ||
| example: 'subscriber-123', | ||
| }) | ||
| @IsString() | ||
| @IsDefined() | ||
| @IsNotEmpty({ message: 'subscriberId is required for link_user' }) | ||
| subscriberId: string; | ||
|
|
||
| @ApiProperty({ | ||
| type: String, | ||
| description: 'Integration identifier', | ||
| }) | ||
| @IsString() | ||
| @IsDefined() | ||
| @IsNotEmpty({ message: 'Integration identifier is required' }) | ||
| integrationIdentifier: string; | ||
|
|
||
| @ApiPropertyOptional({ | ||
| type: String, | ||
| description: | ||
| 'Identifier of the existing channel connection to associate this user endpoint with. ' + | ||
| 'Generated automatically if not provided.', | ||
| example: 'slack-connection-abc123', | ||
| }) | ||
| @IsString() | ||
| @IsOptional() | ||
| connectionIdentifier?: string; | ||
|
|
||
| @ApiContextPayload() | ||
| @IsOptional() | ||
| @IsValidContextPayload({ maxCount: 5 }) | ||
| context?: ContextPayload; | ||
|
|
||
| @ApiPropertyOptional({ | ||
| type: [String], | ||
| description: | ||
| `**Slack only**: User-level OAuth scopes for "Sign in with Slack". ` + | ||
| `Defaults to: ${SLACK_LINK_USER_OAUTH_SCOPES.join(', ')}. ` + | ||
| `**MS Teams**: ignored — uses delegated OpenID scopes (openid, profile, User.Read).`, | ||
| example: ['identity.basic'], | ||
| }) | ||
| @IsOptional() | ||
| @IsArray() | ||
| @IsString({ each: true }) | ||
| userScope?: string[]; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Normalize
sharedconnect requests before generating the OAuth URL.This endpoint exposes
connectionMode, but the MS Teams path downstream drops that field and only seessubscriberId,context, andautoLinkUser. A caller can therefore sendconnectionMode: 'shared'together withsubscriberIdand/orautoLinkUser: true, and the request is no longer guaranteed to stay tenant/context-scoped. Please enforce the mode here (or in the connect use case) by omitting/rejectingsubscriberIdforsharedand forcingautoLinkUsertofalseserver-side.Suggested normalization
📝 Committable suggestion
🤖 Prompt for AI Agents