• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

microsoft / botbuilder-js / 3698619628

pending completion
3698619628

Pull #4389

github

GitHub
Merge 33a6b02b4 into e5f7e3abe
Pull Request #4389: chore(deps): bump express from 4.17.1 to 4.17.3

9686 of 12680 branches covered (76.39%)

Branch coverage included in aggregate %.

19968 of 22357 relevant lines covered (89.31%)

3197.88 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

16.95
/libraries/botframework-connector/src/auth/parameterizedBotFrameworkAuthentication.ts
1
// Copyright (c) Microsoft Corporation.
2
// Licensed under the MIT License.
3

4
import { Activity, Channels, RoleTypes, StatusCodes } from 'botframework-schema';
1✔
5
import { AuthenticateRequestResult } from './authenticateRequestResult';
6
import type { AuthenticationConfiguration } from './authenticationConfiguration';
7
import { AuthenticationConstants } from './authenticationConstants';
1✔
8
import { AuthenticationError } from './authenticationError';
1✔
9
import { BotFrameworkAuthentication } from './botFrameworkAuthentication';
1✔
10
import { ConnectorClientOptions } from '../connectorApi/models';
11
import type { ConnectorFactory } from './connectorFactory';
12
import { ConnectorFactoryImpl } from './connectorFactoryImpl';
1✔
13
import type { BotFrameworkClient } from '../skills';
14
import { BotFrameworkClientImpl } from './botFrameworkClientImpl';
1✔
15
import { Claim, ClaimsIdentity } from './claimsIdentity';
1✔
16
import { EmulatorValidation } from './emulatorValidation';
1✔
17
import { JwtTokenExtractor } from './jwtTokenExtractor';
1✔
18
import { JwtTokenValidation } from './jwtTokenValidation';
1✔
19
import type { ServiceClientCredentialsFactory } from './serviceClientCredentialsFactory';
20
import { SkillValidation } from './skillValidation';
1✔
21
import { ToBotFromBotOrEmulatorTokenValidationParameters } from './tokenValidationParameters';
1✔
22
import { UserTokenClientImpl } from './userTokenClientImpl';
1✔
23
import type { UserTokenClient } from './userTokenClient';
24
import { VerifyOptions } from 'jsonwebtoken';
25

26
function getAppId(claimsIdentity: ClaimsIdentity): string | undefined {
27
    // For requests from channel App Id is in Audience claim of JWT token. For emulator it is in AppId claim. For
28
    // unauthenticated requests we have anonymous claimsIdentity provided auth is disabled.
29
    // For Activities coming from Emulator AppId claim contains the Bot's AAD AppId.
30
    return (
10✔
31
        claimsIdentity.getClaimValue(AuthenticationConstants.AudienceClaim) ??
10✔
32
        claimsIdentity.getClaimValue(AuthenticationConstants.AppIdClaim) ??
10!
33
        undefined
34
    );
35
}
36

37
/**
38
 * @internal
39
 * Parameterized [BotFrameworkAuthentication](xref:botframework-connector.BotFrameworkAuthentication) used to authenticate Bot Framework Protocol network calls within this environment.
40
 */
41
export class ParameterizedBotFrameworkAuthentication extends BotFrameworkAuthentication {
1✔
42
    /**
43
     * @param validateAuthority The validate authority value to use.
44
     * @param toChannelFromBotLoginUrl The to Channel from bot login url.
45
     * @param toChannelFromBotOAuthScope The to Channel from bot oauth scope.
46
     * @param toBotFromChannelTokenIssuer The to bot from Channel Token Issuer.
47
     * @param oAuthUrl The OAuth url.
48
     * @param toBotFromChannelOpenIdMetadataUrl The to bot from Channel Open Id Metadata url.
49
     * @param toBotFromEmulatorOpenIdMetadataUrl The to bot from Emulator Open Id Metadata url.
50
     * @param callerId The callerId set on an authenticated [Activities](xref:botframework-schema.Activity).
51
     * @param credentialsFactory The [ServiceClientCredentialsFactory](xref:botframework-connector.ServiceClientCredentialsFactory) to use to create credentials.
52
     * @param authConfiguration The [AuthenticationConfiguration](xref:botframework-connector.AuthenticationConfiguration) to use.
53
     * @param botFrameworkClientFetch The fetch to use in BotFrameworkClient.
54
     * @param connectorClientOptions The [ConnectorClientOptions](xref:botframework-connector.ConnectorClientOptions) to use when creating ConnectorClients.
55
     */
56
    constructor(
57
        private readonly validateAuthority: boolean,
17✔
58
        private readonly toChannelFromBotLoginUrl: string,
17✔
59
        private readonly toChannelFromBotOAuthScope: string,
17✔
60
        private readonly toBotFromChannelTokenIssuer: string,
17✔
61
        private readonly oAuthUrl: string,
17✔
62
        private readonly toBotFromChannelOpenIdMetadataUrl: string,
17✔
63
        private readonly toBotFromEmulatorOpenIdMetadataUrl: string,
17✔
64
        private readonly callerId: string,
17✔
65
        private readonly credentialsFactory: ServiceClientCredentialsFactory,
17✔
66
        private readonly authConfiguration: AuthenticationConfiguration,
17✔
67
        private readonly botFrameworkClientFetch?: (input: RequestInfo, init?: RequestInit) => Promise<Response>,
17✔
68
        private readonly connectorClientOptions: ConnectorClientOptions = {}
17!
69
    ) {
70
        super();
17✔
71
    }
72

73
    /**
74
     * Gets the originating audience from Bot OAuth scope.
75
     *
76
     * @returns The originating audience.
77
     */
78
    getOriginatingAudience(): string {
79
        return this.toChannelFromBotOAuthScope;
4✔
80
    }
81

82
    /**
83
     * @param authHeader The http auth header received in the skill request.
84
     * @returns The identity validation result.
85
     */
86
    async authenticateChannelRequest(authHeader: string): Promise<ClaimsIdentity> {
87
        if (!authHeader.trim()) {
×
88
            const isAuthDisabled = await this.credentialsFactory.isAuthenticationDisabled();
×
89
            if (!isAuthDisabled) {
×
90
                throw new AuthenticationError(
×
91
                    'Unauthorized Access. Request is not authorized',
92
                    StatusCodes.UNAUTHORIZED
93
                );
94
            }
95

96
            // In the scenario where auth is disabled, we still want to have the isAuthenticated flag set in the
97
            // ClaimsIdentity. To do this requires adding in an empty claim. Since ChannelServiceHandler calls are
98
            // always a skill callback call, we set the skill claim too.
99
            return SkillValidation.createAnonymousSkillClaim();
×
100
        }
101

102
        return this.JwtTokenValidation_validateAuthHeader(authHeader, 'unknown', null);
×
103
    }
104

105
    /**
106
     * Validate Bot Framework Protocol requests.
107
     *
108
     * @param activity The inbound Activity.
109
     * @param authHeader The http auth header received in the skill request.
110
     * @returns Promise with AuthenticateRequestResult.
111
     */
112
    async authenticateRequest(activity: Activity, authHeader: string): Promise<AuthenticateRequestResult> {
113
        const claimsIdentity = await this.JwtTokenValidation_authenticateRequest(activity, authHeader);
×
114

115
        const outboundAudience = SkillValidation.isSkillClaim(claimsIdentity.claims)
×
116
            ? JwtTokenValidation.getAppIdFromClaims(claimsIdentity.claims)
×
117
            : this.toChannelFromBotOAuthScope;
118

119
        const callerId = await this.generateCallerId(this.credentialsFactory, claimsIdentity, this.callerId);
×
120

121
        const connectorFactory = new ConnectorFactoryImpl(
×
122
            getAppId(claimsIdentity),
123
            this.toChannelFromBotOAuthScope,
124
            this.toChannelFromBotLoginUrl,
125
            this.validateAuthority,
126
            this.credentialsFactory
127
        );
128

129
        return {
×
130
            audience: outboundAudience,
131
            callerId,
132
            claimsIdentity,
133
            connectorFactory,
134
        };
135
    }
136

137
    /**
138
     * Validate Bot Framework Protocol requests.
139
     *
140
     * @param authHeader The http auth header received in the skill request.
141
     * @param channelIdHeader The channel Id HTTP header.
142
     * @returns Promise with AuthenticateRequestResult.
143
     */
144
    async authenticateStreamingRequest(
145
        authHeader: string,
146
        channelIdHeader: string
147
    ): Promise<AuthenticateRequestResult> {
148
        if (!channelIdHeader?.trim() && !(await this.credentialsFactory.isAuthenticationDisabled())) {
×
149
            throw new AuthenticationError("'channelIdHeader' required.", StatusCodes.UNAUTHORIZED);
×
150
        }
151

152
        const claimsIdentity = await this.JwtTokenValidation_validateAuthHeader(authHeader, channelIdHeader, null);
×
153
        const outboundAudience = SkillValidation.isSkillClaim(claimsIdentity.claims)
×
154
            ? JwtTokenValidation.getAppIdFromClaims(claimsIdentity.claims)
×
155
            : this.toChannelFromBotOAuthScope;
156
        const callerId = await this.generateCallerId(this.credentialsFactory, claimsIdentity, this.callerId);
×
157

158
        return { audience: outboundAudience, callerId, claimsIdentity };
×
159
    }
160

161
    /**
162
     * Creates the appropriate UserTokenClient instance.
163
     *
164
     * @param claimsIdentity The inbound Activity's ClaimsIdentity.
165
     * @returns Promise with UserTokenClient instance.
166
     */
167
    async createUserTokenClient(claimsIdentity: ClaimsIdentity): Promise<UserTokenClient> {
168
        const appId = getAppId(claimsIdentity);
7✔
169
        const credentials = await this.credentialsFactory.createCredentials(
7✔
170
            appId,
171
            this.toChannelFromBotOAuthScope,
172
            this.toChannelFromBotLoginUrl,
173
            this.validateAuthority
174
        );
175

176
        return new UserTokenClientImpl(appId, credentials, this.oAuthUrl, this.connectorClientOptions);
7✔
177
    }
178

179
    /**
180
     * Creates a ConnectorFactory that can be used to create ConnectorClients that can use credentials from this particular Cloud Environment.
181
     *
182
     * @param claimsIdentity The inbound Activity's ClaimsIdentity.
183
     * @returns A ConnectorFactory.
184
     */
185
    createConnectorFactory(claimsIdentity: ClaimsIdentity): ConnectorFactory {
186
        return new ConnectorFactoryImpl(
3✔
187
            getAppId(claimsIdentity),
188
            this.toChannelFromBotOAuthScope,
189
            this.toChannelFromBotLoginUrl,
190
            this.validateAuthority,
191
            this.credentialsFactory
192
        );
193
    }
194

195
    /**
196
     * Creates a BotFrameworkClient used for calling Skills.
197
     *
198
     * @returns A BotFrameworkClient instance to call Skills.
199
     */
200
    createBotFrameworkClient(): BotFrameworkClient {
201
        return new BotFrameworkClientImpl(
×
202
            this.credentialsFactory,
203
            this.toChannelFromBotLoginUrl,
204
            this.botFrameworkClientFetch
205
        );
206
    }
207

208
    private async JwtTokenValidation_authenticateRequest(
209
        activity: Partial<Activity>,
210
        authHeader: string
211
    ): Promise<ClaimsIdentity> {
212
        if (!authHeader.trim()) {
×
213
            const isAuthDisabled = await this.credentialsFactory.isAuthenticationDisabled();
×
214
            if (!isAuthDisabled) {
×
215
                throw new AuthenticationError(
×
216
                    'Unauthorized Access. Request is not authorized',
217
                    StatusCodes.UNAUTHORIZED
218
                );
219
            }
220

221
            // Check if the activity is for a skill call and is coming from the Emulator.
222
            if (activity.channelId === Channels.Emulator && activity.recipient?.role === RoleTypes.Skill) {
×
223
                return SkillValidation.createAnonymousSkillClaim();
×
224
            }
225

226
            // In the scenario where Auth is disabled, we still want to have the
227
            // IsAuthenticated flag set in the ClaimsIdentity. To do this requires
228
            // adding in an empty claim.
229
            return new ClaimsIdentity([], AuthenticationConstants.AnonymousAuthType);
×
230
        }
231

232
        const claimsIdentity: ClaimsIdentity = await this.JwtTokenValidation_validateAuthHeader(
×
233
            authHeader,
234
            activity.channelId,
235
            activity.serviceUrl
236
        );
237

238
        return claimsIdentity;
×
239
    }
240

241
    private async JwtTokenValidation_validateAuthHeader(
242
        authHeader: string,
243
        channelId: string,
244
        serviceUrl = ''
×
245
    ): Promise<ClaimsIdentity> {
246
        const identity = await this.JwtTokenValidation_authenticateToken(authHeader, channelId, serviceUrl);
×
247

248
        await this.JwtTokenValidation_validateClaims(identity.claims);
×
249

250
        return identity;
×
251
    }
252

253
    private async JwtTokenValidation_validateClaims(claims: Claim[] = []): Promise<void> {
×
254
        if (this.authConfiguration.validateClaims) {
×
255
            // Call the validation method if defined (it should throw an exception if the validation fails)
256
            await this.authConfiguration.validateClaims(claims);
×
257
        } else if (SkillValidation.isSkillClaim(claims)) {
×
258
            // Skill claims must be validated using AuthenticationConfiguration validateClaims
259
            throw new AuthenticationError(
×
260
                'Unauthorized Access. Request is not authorized. Skill Claims require validation.',
261
                StatusCodes.UNAUTHORIZED
262
            );
263
        }
264
    }
265

266
    private async JwtTokenValidation_authenticateToken(
267
        authHeader: string,
268
        channelId: string,
269
        serviceUrl: string
270
    ): Promise<ClaimsIdentity | undefined> {
271
        if (SkillValidation.isSkillToken(authHeader)) {
×
272
            return this.SkillValidation_authenticateChannelToken(authHeader, channelId);
×
273
        }
274

275
        if (EmulatorValidation.isTokenFromEmulator(authHeader)) {
×
276
            return this.EmulatorValidation_authenticateEmulatorToken(authHeader, channelId);
×
277
        }
278

279
        // Handle requests from BotFramework Channels
280
        return this.ChannelValidation_authenticateChannelToken(authHeader, serviceUrl, channelId);
×
281
    }
282

283
    private async SkillValidation_authenticateChannelToken(
284
        authHeader: string,
285
        channelId: string
286
    ): Promise<ClaimsIdentity> {
287
        // Add allowed token issuers from configuration.
288
        const verifyOptions: VerifyOptions = {
×
289
            ...ToBotFromBotOrEmulatorTokenValidationParameters,
290
            issuer: [
291
                ...ToBotFromBotOrEmulatorTokenValidationParameters.issuer,
292
                ...(this.authConfiguration.validTokenIssuers ?? []),
×
293
            ],
294
        };
295

296
        const tokenExtractor = new JwtTokenExtractor(
×
297
            verifyOptions,
298
            this.toBotFromEmulatorOpenIdMetadataUrl,
299
            AuthenticationConstants.AllowedSigningAlgorithms
300
        );
301

302
        const parts: string[] = authHeader.split(' ');
×
303
        const identity = await tokenExtractor.getIdentity(
×
304
            parts[0],
305
            parts[1],
306
            channelId,
307
            this.authConfiguration.requiredEndorsements
308
        );
309

310
        await this.SkillValidation_ValidateIdentity(identity);
×
311

312
        return identity;
×
313
    }
314

315
    private async SkillValidation_ValidateIdentity(identity: ClaimsIdentity): Promise<void> {
316
        if (!identity) {
×
317
            // No valid identity. Not Authorized.
318
            throw new AuthenticationError(
×
319
                'SkillValidation.validateIdentity(): Invalid identity',
320
                StatusCodes.UNAUTHORIZED
321
            );
322
        }
323

324
        if (!identity.isAuthenticated) {
×
325
            // The token is in some way invalid. Not Authorized.
326
            throw new AuthenticationError(
×
327
                'SkillValidation.validateIdentity(): Token not authenticated',
328
                StatusCodes.UNAUTHORIZED
329
            );
330
        }
331

332
        const versionClaim = identity.getClaimValue(AuthenticationConstants.VersionClaim);
×
333
        if (!versionClaim) {
×
334
            // No version claim
335
            throw new AuthenticationError(
×
336
                `SkillValidation.validateIdentity(): '${AuthenticationConstants.VersionClaim}' claim is required on skill Tokens.`,
337
                StatusCodes.UNAUTHORIZED
338
            );
339
        }
340

341
        // Look for the "aud" claim, but only if issued from the Bot Framework
342
        const audienceClaim = identity.getClaimValue(AuthenticationConstants.AudienceClaim);
×
343
        if (!audienceClaim) {
×
344
            // Claim is not present or doesn't have a value. Not Authorized.
345
            throw new AuthenticationError(
×
346
                `SkillValidation.validateIdentity(): '${AuthenticationConstants.AudienceClaim}' claim is required on skill Tokens.`,
347
                StatusCodes.UNAUTHORIZED
348
            );
349
        }
350

351
        if (!(await this.credentialsFactory.isValidAppId(audienceClaim))) {
×
352
            // The AppId is not valid. Not Authorized.
353
            throw new AuthenticationError(
×
354
                'SkillValidation.validateIdentity(): Invalid audience.',
355
                StatusCodes.UNAUTHORIZED
356
            );
357
        }
358

359
        const appId = JwtTokenValidation.getAppIdFromClaims(identity.claims);
×
360
        if (!appId) {
×
361
            // Invalid appId
362
            throw new AuthenticationError(
×
363
                'SkillValidation.validateIdentity(): Invalid appId.',
364
                StatusCodes.UNAUTHORIZED
365
            );
366
        }
367
    }
368

369
    private async EmulatorValidation_authenticateEmulatorToken(
370
        authHeader: string,
371
        channelId: string
372
    ): Promise<ClaimsIdentity> {
373
        // Add allowed token issuers from configuration.
374
        const verifyOptions: VerifyOptions = {
×
375
            ...ToBotFromBotOrEmulatorTokenValidationParameters,
376
            issuer: [
377
                ...ToBotFromBotOrEmulatorTokenValidationParameters.issuer,
378
                ...(this.authConfiguration.validTokenIssuers ?? []),
×
379
            ],
380
        };
381

382
        const tokenExtractor: JwtTokenExtractor = new JwtTokenExtractor(
×
383
            verifyOptions,
384
            this.toBotFromEmulatorOpenIdMetadataUrl,
385
            AuthenticationConstants.AllowedSigningAlgorithms
386
        );
387

388
        const identity: ClaimsIdentity = await tokenExtractor.getIdentityFromAuthHeader(
×
389
            authHeader,
390
            channelId,
391
            this.authConfiguration.requiredEndorsements
392
        );
393
        if (!identity) {
×
394
            // No valid identity. Not Authorized.
395
            throw new AuthenticationError('Unauthorized. No valid identity.', StatusCodes.UNAUTHORIZED);
×
396
        }
397

398
        if (!identity.isAuthenticated) {
×
399
            // The token is in some way invalid. Not Authorized.
400
            throw new AuthenticationError('Unauthorized. Is not authenticated', StatusCodes.UNAUTHORIZED);
×
401
        }
402

403
        // Now check that the AppID in the claimset matches
404
        // what we're looking for. Note that in a multi-tenant bot, this value
405
        // comes from developer code that may be reaching out to a service, hence the
406
        // Async validation.
407
        const versionClaim: string = identity.getClaimValue(AuthenticationConstants.VersionClaim);
×
408
        if (versionClaim === null) {
×
409
            throw new AuthenticationError(
×
410
                'Unauthorized. "ver" claim is required on Emulator Tokens.',
411
                StatusCodes.UNAUTHORIZED
412
            );
413
        }
414

415
        let appId = '';
×
416

417
        // The Emulator, depending on Version, sends the AppId via either the
418
        // appid claim (Version 1) or the Authorized Party claim (Version 2).
419
        if (!versionClaim || versionClaim === '1.0') {
×
420
            // either no Version or a version of "1.0" means we should look for
421
            // the claim in the "appid" claim.
422
            const appIdClaim: string = identity.getClaimValue(AuthenticationConstants.AppIdClaim);
×
423
            if (!appIdClaim) {
×
424
                // No claim around AppID. Not Authorized.
425
                throw new AuthenticationError(
×
426
                    'Unauthorized. "appid" claim is required on Emulator Token version "1.0".',
427
                    StatusCodes.UNAUTHORIZED
428
                );
429
            }
430

431
            appId = appIdClaim;
×
432
        } else if (versionClaim === '2.0') {
×
433
            // Emulator, "2.0" puts the AppId in the "azp" claim.
434
            const appZClaim: string = identity.getClaimValue(AuthenticationConstants.AuthorizedParty);
×
435
            if (!appZClaim) {
×
436
                // No claim around AppID. Not Authorized.
437
                throw new AuthenticationError(
×
438
                    'Unauthorized. "azp" claim is required on Emulator Token version "2.0".',
439
                    StatusCodes.UNAUTHORIZED
440
                );
441
            }
442

443
            appId = appZClaim;
×
444
        } else {
445
            // Unknown Version. Not Authorized.
446
            throw new AuthenticationError(
×
447
                `Unauthorized. Unknown Emulator Token version "${versionClaim}".`,
448
                StatusCodes.UNAUTHORIZED
449
            );
450
        }
451

452
        if (!(await this.credentialsFactory.isValidAppId(appId))) {
×
453
            throw new AuthenticationError(
×
454
                `Unauthorized. Invalid AppId passed on token: ${appId}`,
455
                StatusCodes.UNAUTHORIZED
456
            );
457
        }
458

459
        return identity;
×
460
    }
461

462
    private async ChannelValidation_authenticateChannelToken(
463
        authHeader: string,
464
        serviceUrl: string,
465
        channelId: string
466
    ): Promise<ClaimsIdentity> {
467
        const tokenValidationParameters = this.ChannelValidation_GetTokenValidationParameters();
×
468
        const tokenExtractor: JwtTokenExtractor = new JwtTokenExtractor(
×
469
            tokenValidationParameters,
470
            this.toBotFromChannelOpenIdMetadataUrl,
471
            AuthenticationConstants.AllowedSigningAlgorithms
472
        );
473

474
        const identity: ClaimsIdentity = await tokenExtractor.getIdentityFromAuthHeader(
×
475
            authHeader,
476
            channelId,
477
            this.authConfiguration.requiredEndorsements
478
        );
479

480
        return this.governmentChannelValidation_ValidateIdentity(identity, serviceUrl);
×
481
    }
482

483
    private ChannelValidation_GetTokenValidationParameters(): VerifyOptions {
484
        return {
×
485
            issuer: [this.toBotFromChannelTokenIssuer],
486
            audience: undefined, // Audience validation takes place manually in code.
487
            clockTolerance: 5 * 60,
488
            ignoreExpiration: false,
489
        };
490
    }
491

492
    private async governmentChannelValidation_ValidateIdentity(
493
        identity: ClaimsIdentity,
494
        serviceUrl: string
495
    ): Promise<ClaimsIdentity> {
496
        if (!identity) {
×
497
            // No valid identity. Not Authorized.
498
            throw new AuthenticationError('Unauthorized. No valid identity.', StatusCodes.UNAUTHORIZED);
×
499
        }
500

501
        if (!identity.isAuthenticated) {
×
502
            // The token is in some way invalid. Not Authorized.
503
            throw new AuthenticationError('Unauthorized. Is not authenticated', StatusCodes.UNAUTHORIZED);
×
504
        }
505

506
        // Now check that the AppID in the claimset matches
507
        // what we're looking for. Note that in a multi-tenant bot, this value
508
        // comes from developer code that may be reaching out to a service, hence the
509
        // Async validation.
510

511
        // Look for the "aud" claim, but only if issued from the Bot Framework
512
        if (identity.getClaimValue(AuthenticationConstants.IssuerClaim) !== this.toBotFromChannelTokenIssuer) {
×
513
            // The relevant Audiance Claim MUST be present. Not Authorized.
514
            throw new AuthenticationError('Unauthorized. Issuer Claim MUST be present.', StatusCodes.UNAUTHORIZED);
×
515
        }
516

517
        // The AppId from the claim in the token must match the AppId specified by the developer.
518
        // In this case, the token is destined for the app, so we find the app ID in the audience claim.
519
        const audClaim: string = identity.getClaimValue(AuthenticationConstants.AudienceClaim);
×
520
        if (!(await this.credentialsFactory.isValidAppId(audClaim || ''))) {
×
521
            // The AppId is not valid or not present. Not Authorized.
522
            throw new AuthenticationError(
×
523
                `Unauthorized. Invalid AppId passed on token: ${audClaim}`,
524
                StatusCodes.UNAUTHORIZED
525
            );
526
        }
527

528
        if (serviceUrl) {
×
529
            const serviceUrlClaim = identity.getClaimValue(AuthenticationConstants.ServiceUrlClaim);
×
530
            if (serviceUrlClaim !== serviceUrl) {
×
531
                // Claim must match. Not Authorized.
532
                throw new AuthenticationError('Unauthorized. ServiceUrl claim do not match.', StatusCodes.UNAUTHORIZED);
×
533
            }
534
        }
535

536
        return identity;
×
537
    }
538
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc