@ -27,7 +27,6 @@ FRONT_PORT=3001
|
|||||||
# IS_MULTIWORKSPACE_ENABLED=false
|
# IS_MULTIWORKSPACE_ENABLED=false
|
||||||
# AUTH_MICROSOFT_ENABLED=false
|
# AUTH_MICROSOFT_ENABLED=false
|
||||||
# AUTH_MICROSOFT_CLIENT_ID=replace_me_with_azure_client_id
|
# AUTH_MICROSOFT_CLIENT_ID=replace_me_with_azure_client_id
|
||||||
# AUTH_MICROSOFT_TENANT_ID=replace_me_with_azure_tenant_id
|
|
||||||
# AUTH_MICROSOFT_CLIENT_SECRET=replace_me_with_azure_client_secret
|
# AUTH_MICROSOFT_CLIENT_SECRET=replace_me_with_azure_client_secret
|
||||||
# AUTH_MICROSOFT_CALLBACK_URL=http://localhost:3000/auth/microsoft/redirect
|
# AUTH_MICROSOFT_CALLBACK_URL=http://localhost:3000/auth/microsoft/redirect
|
||||||
# AUTH_MICROSOFT_APIS_CALLBACK_URL=http://localhost:3000/auth/microsoft-apis/get-access-token
|
# AUTH_MICROSOFT_APIS_CALLBACK_URL=http://localhost:3000/auth/microsoft-apis/get-access-token
|
||||||
|
|||||||
@ -14,6 +14,7 @@ export enum AuthExceptionCode {
|
|||||||
WORKSPACE_NOT_FOUND = 'WORKSPACE_NOT_FOUND',
|
WORKSPACE_NOT_FOUND = 'WORKSPACE_NOT_FOUND',
|
||||||
INVALID_INPUT = 'INVALID_INPUT',
|
INVALID_INPUT = 'INVALID_INPUT',
|
||||||
FORBIDDEN_EXCEPTION = 'FORBIDDEN_EXCEPTION',
|
FORBIDDEN_EXCEPTION = 'FORBIDDEN_EXCEPTION',
|
||||||
|
INSUFFICIENT_SCOPES = 'INSUFFICIENT_SCOPES',
|
||||||
UNAUTHENTICATED = 'UNAUTHENTICATED',
|
UNAUTHENTICATED = 'UNAUTHENTICATED',
|
||||||
INVALID_DATA = 'INVALID_DATA',
|
INVALID_DATA = 'INVALID_DATA',
|
||||||
INTERNAL_SERVER_ERROR = 'INTERNAL_SERVER_ERROR',
|
INTERNAL_SERVER_ERROR = 'INTERNAL_SERVER_ERROR',
|
||||||
|
|||||||
@ -1,6 +1,10 @@
|
|||||||
import { ExecutionContext, Injectable } from '@nestjs/common';
|
import { ExecutionContext, Injectable } from '@nestjs/common';
|
||||||
import { AuthGuard } from '@nestjs/passport';
|
import { AuthGuard } from '@nestjs/passport';
|
||||||
|
|
||||||
|
import {
|
||||||
|
AuthException,
|
||||||
|
AuthExceptionCode,
|
||||||
|
} from 'src/engine/core-modules/auth/auth.exception';
|
||||||
import { MicrosoftAPIsOauthExchangeCodeForTokenStrategy } from 'src/engine/core-modules/auth/strategies/microsoft-apis-oauth-exchange-code-for-token.auth.strategy';
|
import { MicrosoftAPIsOauthExchangeCodeForTokenStrategy } from 'src/engine/core-modules/auth/strategies/microsoft-apis-oauth-exchange-code-for-token.auth.strategy';
|
||||||
import { setRequestExtraParams } from 'src/engine/core-modules/auth/utils/google-apis-set-request-extra-params.util';
|
import { setRequestExtraParams } from 'src/engine/core-modules/auth/utils/google-apis-set-request-extra-params.util';
|
||||||
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
|
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
|
||||||
@ -14,18 +18,31 @@ export class MicrosoftAPIsOauthExchangeCodeForTokenGuard extends AuthGuard(
|
|||||||
}
|
}
|
||||||
|
|
||||||
async canActivate(context: ExecutionContext) {
|
async canActivate(context: ExecutionContext) {
|
||||||
const request = context.switchToHttp().getRequest();
|
try {
|
||||||
const state = JSON.parse(request.query.state);
|
const request = context.switchToHttp().getRequest();
|
||||||
|
const state = JSON.parse(request.query.state);
|
||||||
|
|
||||||
new MicrosoftAPIsOauthExchangeCodeForTokenStrategy(this.environmentService);
|
new MicrosoftAPIsOauthExchangeCodeForTokenStrategy(
|
||||||
|
this.environmentService,
|
||||||
|
);
|
||||||
|
|
||||||
setRequestExtraParams(request, {
|
setRequestExtraParams(request, {
|
||||||
transientToken: state.transientToken,
|
transientToken: state.transientToken,
|
||||||
redirectLocation: state.redirectLocation,
|
redirectLocation: state.redirectLocation,
|
||||||
calendarVisibility: state.calendarVisibility,
|
calendarVisibility: state.calendarVisibility,
|
||||||
messageVisibility: state.messageVisibility,
|
messageVisibility: state.messageVisibility,
|
||||||
});
|
});
|
||||||
|
|
||||||
return (await super.canActivate(context)) as boolean;
|
return (await super.canActivate(context)) as boolean;
|
||||||
|
} catch (error) {
|
||||||
|
if (error?.oauthError?.statusCode === 403) {
|
||||||
|
throw new AuthException(
|
||||||
|
`Insufficient privileges to access this microsoft resource. Make sure you have the correct scopes or ask your admin to update your scopes. ${error?.message}`,
|
||||||
|
AuthExceptionCode.INSUFFICIENT_SCOPES,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,7 +22,7 @@ export class MicrosoftAPIsOauthCommonStrategy extends PassportStrategy(
|
|||||||
super({
|
super({
|
||||||
clientID: environmentService.get('AUTH_MICROSOFT_CLIENT_ID'),
|
clientID: environmentService.get('AUTH_MICROSOFT_CLIENT_ID'),
|
||||||
clientSecret: environmentService.get('AUTH_MICROSOFT_CLIENT_SECRET'),
|
clientSecret: environmentService.get('AUTH_MICROSOFT_CLIENT_SECRET'),
|
||||||
tenant: environmentService.get('AUTH_MICROSOFT_TENANT_ID'),
|
tenant: 'common',
|
||||||
callbackURL: environmentService.get('AUTH_MICROSOFT_APIS_CALLBACK_URL'),
|
callbackURL: environmentService.get('AUTH_MICROSOFT_APIS_CALLBACK_URL'),
|
||||||
scope: scopes,
|
scope: scopes,
|
||||||
passReqToCallback: true,
|
passReqToCallback: true,
|
||||||
|
|||||||
@ -32,7 +32,7 @@ export class MicrosoftStrategy extends PassportStrategy(Strategy, 'microsoft') {
|
|||||||
clientID: environmentService.get('AUTH_MICROSOFT_CLIENT_ID'),
|
clientID: environmentService.get('AUTH_MICROSOFT_CLIENT_ID'),
|
||||||
clientSecret: environmentService.get('AUTH_MICROSOFT_CLIENT_SECRET'),
|
clientSecret: environmentService.get('AUTH_MICROSOFT_CLIENT_SECRET'),
|
||||||
callbackURL: environmentService.get('AUTH_MICROSOFT_CALLBACK_URL'),
|
callbackURL: environmentService.get('AUTH_MICROSOFT_CALLBACK_URL'),
|
||||||
tenant: environmentService.get('AUTH_MICROSOFT_TENANT_ID'),
|
tenant: 'common',
|
||||||
scope: ['user.read'],
|
scope: ['user.read'],
|
||||||
passReqToCallback: true,
|
passReqToCallback: true,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -6,6 +6,7 @@ export const getMicrosoftApisOauthScopes = () => {
|
|||||||
'offline_access',
|
'offline_access',
|
||||||
'Mail.Read',
|
'Mail.Read',
|
||||||
'Calendars.Read',
|
'Calendars.Read',
|
||||||
|
'User.Read',
|
||||||
];
|
];
|
||||||
|
|
||||||
return scopes;
|
return scopes;
|
||||||
|
|||||||
@ -204,10 +204,6 @@ export class EnvironmentVariables {
|
|||||||
@ValidateIf((env) => env.AUTH_MICROSOFT_ENABLED)
|
@ValidateIf((env) => env.AUTH_MICROSOFT_ENABLED)
|
||||||
AUTH_MICROSOFT_CLIENT_ID: string;
|
AUTH_MICROSOFT_CLIENT_ID: string;
|
||||||
|
|
||||||
@IsString()
|
|
||||||
@ValidateIf((env) => env.AUTH_MICROSOFT_ENABLED)
|
|
||||||
AUTH_MICROSOFT_TENANT_ID: string;
|
|
||||||
|
|
||||||
@IsString()
|
@IsString()
|
||||||
@ValidateIf((env) => env.AUTH_MICROSOFT_ENABLED)
|
@ValidateIf((env) => env.AUTH_MICROSOFT_ENABLED)
|
||||||
AUTH_MICROSOFT_CLIENT_SECRET: string;
|
AUTH_MICROSOFT_CLIENT_SECRET: string;
|
||||||
|
|||||||
@ -17,10 +17,6 @@ export class MicrosoftOAuth2ClientManagerService {
|
|||||||
callback: AuthProviderCallback,
|
callback: AuthProviderCallback,
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
const tenantId = this.environmentService.get(
|
|
||||||
'AUTH_MICROSOFT_TENANT_ID',
|
|
||||||
);
|
|
||||||
|
|
||||||
const urlData = new URLSearchParams();
|
const urlData = new URLSearchParams();
|
||||||
|
|
||||||
urlData.append(
|
urlData.append(
|
||||||
@ -36,7 +32,7 @@ export class MicrosoftOAuth2ClientManagerService {
|
|||||||
urlData.append('grant_type', 'refresh_token');
|
urlData.append('grant_type', 'refresh_token');
|
||||||
|
|
||||||
const res = await fetch(
|
const res = await fetch(
|
||||||
`https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`,
|
`https://login.microsoftonline.com/common/oauth2/v2.0/token`,
|
||||||
{
|
{
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: urlData,
|
body: urlData,
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/s
|
|||||||
import { Contact } from 'src/modules/contact-creation-manager/types/contact.type';
|
import { Contact } from 'src/modules/contact-creation-manager/types/contact.type';
|
||||||
import { getDomainNameFromHandle } from 'src/modules/contact-creation-manager/utils/get-domain-name-from-handle.util';
|
import { getDomainNameFromHandle } from 'src/modules/contact-creation-manager/utils/get-domain-name-from-handle.util';
|
||||||
import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity';
|
import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity';
|
||||||
|
import { isWorkDomain } from 'src/utils/is-work-email';
|
||||||
|
|
||||||
export function filterOutSelfAndContactsFromCompanyOrWorkspace(
|
export function filterOutSelfAndContactsFromCompanyOrWorkspace(
|
||||||
contacts: Contact[],
|
contacts: Contact[],
|
||||||
@ -21,9 +22,13 @@ export function filterOutSelfAndContactsFromCompanyOrWorkspace(
|
|||||||
new Map<string, boolean>(),
|
new Map<string, boolean>(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const isDifferentDomain = (contact: Contact, selfDomainName: string) =>
|
||||||
|
getDomainNameFromHandle(contact.handle) !== selfDomainName;
|
||||||
|
|
||||||
return contacts.filter(
|
return contacts.filter(
|
||||||
(contact) =>
|
(contact) =>
|
||||||
getDomainNameFromHandle(contact.handle) !== selfDomainName &&
|
(isDifferentDomain(contact, selfDomainName) ||
|
||||||
|
!isWorkDomain(selfDomainName)) &&
|
||||||
!workspaceMembersMap[contact.handle] &&
|
!workspaceMembersMap[contact.handle] &&
|
||||||
!handleAliases.includes(contact.handle),
|
!handleAliases.includes(contact.handle),
|
||||||
);
|
);
|
||||||
|
|||||||
@ -8,3 +8,7 @@ export const isWorkEmail = (email: string) => {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const isWorkDomain = (domain: string) => {
|
||||||
|
return !emailProvidersSet.has(domain);
|
||||||
|
};
|
||||||
|
|||||||
@ -79,7 +79,6 @@ Then you can set the following environment variables:
|
|||||||
|
|
||||||
- `AUTH_MICROSOFT_ENABLED=true`
|
- `AUTH_MICROSOFT_ENABLED=true`
|
||||||
- `AUTH_MICROSOFT_CLIENT_ID=<client-id>`
|
- `AUTH_MICROSOFT_CLIENT_ID=<client-id>`
|
||||||
- `AUTH_MICROSOFT_TENANT_ID=<tenant-id>`
|
|
||||||
- `AUTH_MICROSOFT_CLIENT_SECRET=<client-secret>`
|
- `AUTH_MICROSOFT_CLIENT_SECRET=<client-secret>`
|
||||||
- `AUTH_MICROSOFT_CALLBACK_URL=https://<your-domain>/auth/microsoft/redirect` if you want to use Microsoft SSO
|
- `AUTH_MICROSOFT_CALLBACK_URL=https://<your-domain>/auth/microsoft/redirect` if you want to use Microsoft SSO
|
||||||
- `AUTH_MICROSOFT_APIS_CALLBACK_URL=https://<your-domain>/auth/microsoft-apis/get-access-token`
|
- `AUTH_MICROSOFT_APIS_CALLBACK_URL=https://<your-domain>/auth/microsoft-apis/get-access-token`
|
||||||
@ -189,9 +188,8 @@ yarn command:prod cron:calendar:ongoing-stale
|
|||||||
['AUTH_GOOGLE_CALLBACK_URL', 'https://[YourDomain]/auth/google/redirect', 'Google auth callback'],
|
['AUTH_GOOGLE_CALLBACK_URL', 'https://[YourDomain]/auth/google/redirect', 'Google auth callback'],
|
||||||
['AUTH_MICROSOFT_ENABLED', 'false', 'Enable Microsoft SSO login'],
|
['AUTH_MICROSOFT_ENABLED', 'false', 'Enable Microsoft SSO login'],
|
||||||
['AUTH_MICROSOFT_CLIENT_ID', '', 'Microsoft client ID'],
|
['AUTH_MICROSOFT_CLIENT_ID', '', 'Microsoft client ID'],
|
||||||
['AUTH_MICROSOFT_TENANT_ID', '', 'Microsoft tenant ID'],
|
|
||||||
['AUTH_MICROSOFT_CLIENT_SECRET', '', 'Microsoft client secret'],
|
['AUTH_MICROSOFT_CLIENT_SECRET', '', 'Microsoft client secret'],
|
||||||
['AUTH_MICROSOFT_CALLBACK_URL', 'http://[YourDomain]/auth/microsoft/redirect', 'Microsoft auth callback'],
|
['AUTH_MICROSOFT_CALLBACK_URL', 'https://[YourDomain]/auth/microsoft/redirect', 'Microsoft auth callback'],
|
||||||
['AUTH_MICROSOFT_APIS_CALLBACK_URL', 'http://[YourDomain]/auth/microsoft-apis/get-access-token', 'Microsoft APIs auth callback'],
|
['AUTH_MICROSOFT_APIS_CALLBACK_URL', 'http://[YourDomain]/auth/microsoft-apis/get-access-token', 'Microsoft APIs auth callback'],
|
||||||
['IS_MULTIWORKSPACE_ENABLED', 'false', 'Allows the use of multiple workspaces. Requires a web server that can manage wildcards for subdomains.'],
|
['IS_MULTIWORKSPACE_ENABLED', 'false', 'Allows the use of multiple workspaces. Requires a web server that can manage wildcards for subdomains.'],
|
||||||
['PASSWORD_RESET_TOKEN_EXPIRES_IN', '5m', 'Password reset token expiration time'],
|
['PASSWORD_RESET_TOKEN_EXPIRES_IN', '5m', 'Password reset token expiration time'],
|
||||||
|
|||||||
Reference in New Issue
Block a user