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

microsoft / botbuilder-js / 16629378359

30 Jul 2025 05:22PM UTC coverage: 84.484% (+0.01%) from 84.47%
16629378359

Pull #4892

github

web-flow
Merge b64f6a440 into 8dcfcdd78
Pull Request #4892: fix: CodeQL issues with severity High

8267 of 10948 branches covered (75.51%)

Branch coverage included in aggregate %.

23 of 29 new or added lines in 3 files covered. (79.31%)

2 existing lines in 2 files now uncovered.

20580 of 23197 relevant lines covered (88.72%)

3868.46 hits per line

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

27.56
/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
import { AseChannelValidation } from './aseChannelValidation';
1✔
26

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

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

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

83
    /**
84
     * @param authHeader The http auth header received in the skill request.
85
     * @returns The identity validation result.
86
     */
87
    async authenticateChannelRequest(authHeader: string): Promise<ClaimsIdentity> {
NEW
88
        if (await this.credentialsFactory.isAuthenticationDisabled()) {
×
NEW
89
            return SkillValidation.createAnonymousSkillClaim();
×
90
        } else {
NEW
91
            if (!authHeader.trim()) {
×
UNCOV
92
                throw new AuthenticationError(
×
93
                    'Unauthorized Access. Request is not authorized',
94
                    StatusCodes.UNAUTHORIZED,
95
                );
96
            }
NEW
97
            return this.JwtTokenValidation_validateAuthHeader(authHeader, 'unknown', null);
×
98
        }
99
    }
100

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

111
        const outboundAudience = SkillValidation.isSkillClaim(claimsIdentity.claims)
×
112
            ? JwtTokenValidation.getAppIdFromClaims(claimsIdentity.claims)
×
113
            : this.toChannelFromBotOAuthScope;
114

115
        const callerId = await this.generateCallerId(this.credentialsFactory, claimsIdentity, this.callerId);
×
116

117
        const connectorFactory = new ConnectorFactoryImpl(
×
118
            getAppId(claimsIdentity),
119
            this.toChannelFromBotOAuthScope,
120
            this.toChannelFromBotLoginUrl,
121
            this.validateAuthority,
122
            this.credentialsFactory,
123
            this.connectorClientOptions,
124
        );
125

126
        return {
×
127
            audience: outboundAudience,
128
            callerId,
129
            claimsIdentity,
130
            connectorFactory,
131
        };
132
    }
133

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

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

155
        return { audience: outboundAudience, callerId, claimsIdentity };
×
156
    }
157

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

173
        return new UserTokenClientImpl(appId, credentials, this.oAuthUrl, this.connectorClientOptions);
7✔
174
    }
175

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

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

207
    private async JwtTokenValidation_authenticateRequest(
208
        activity: Partial<Activity>,
209
        authHeader: string,
210
    ): Promise<ClaimsIdentity> {
211
        if (await this.credentialsFactory.isAuthenticationDisabled()) {
1!
212
            // Check if the activity is for a skill call and is coming from the Emulator.
213
            if (activity.channelId === Channels.Emulator && activity.recipient?.role === RoleTypes.Skill) {
×
214
                return SkillValidation.createAnonymousSkillClaim();
×
215
            }
216

217
            // In the scenario where Auth is disabled, we still want to have the
218
            // IsAuthenticated flag set in the ClaimsIdentity. To do this requires
219
            // adding in an empty claim.
220
            return new ClaimsIdentity([], AuthenticationConstants.AnonymousAuthType);
×
221
        } else {
222
            if (!authHeader.trim()) {
1!
NEW
223
                throw new AuthenticationError(
×
224
                    'Unauthorized Access. Request is not authorized',
225
                    StatusCodes.UNAUTHORIZED,
226
                );
227
            }
228
            const claimsIdentity: ClaimsIdentity = await this.JwtTokenValidation_validateAuthHeader(
1✔
229
                authHeader,
230
                activity.channelId,
231
                activity.serviceUrl,
232
            );
233

NEW
234
            return claimsIdentity;
×
235
        }
236
    }
237

238
    private async JwtTokenValidation_validateAuthHeader(
239
        authHeader: string,
240
        channelId: string,
241
        serviceUrl = '',
1✔
242
    ): Promise<ClaimsIdentity> {
243
        const identity = await this.JwtTokenValidation_authenticateToken(authHeader, channelId, serviceUrl);
1✔
244

245
        await this.JwtTokenValidation_validateClaims(identity.claims);
×
246

247
        return identity;
×
248
    }
249

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

263
    private async JwtTokenValidation_authenticateToken(
264
        authHeader: string,
265
        channelId: string,
266
        serviceUrl: string,
267
    ): Promise<ClaimsIdentity | undefined> {
268
        if (AseChannelValidation.isTokenFromAseChannel(channelId)) {
1!
269
            return AseChannelValidation.authenticateAseChannelToken(authHeader);
×
270
        }
271

272
        if (SkillValidation.isSkillToken(authHeader)) {
1!
273
            return this.SkillValidation_authenticateChannelToken(authHeader, channelId);
×
274
        }
275

276
        if (EmulatorValidation.isTokenFromEmulator(authHeader)) {
1✔
277
            return this.EmulatorValidation_authenticateEmulatorToken(authHeader, channelId);
1✔
278
        }
279

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

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

297
        const tokenExtractor = new JwtTokenExtractor(
×
298
            verifyOptions,
299
            this.toBotFromEmulatorOpenIdMetadataUrl,
300
            AuthenticationConstants.AllowedSigningAlgorithms,
301
            this.connectorClientOptions?.proxySettings,
×
302
        );
303

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

312
        await this.SkillValidation_ValidateIdentity(identity);
×
313

314
        return identity;
×
315
    }
316

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

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

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

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

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

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

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

384
        const tokenExtractor: JwtTokenExtractor = new JwtTokenExtractor(
1✔
385
            verifyOptions,
386
            this.toBotFromEmulatorOpenIdMetadataUrl,
387
            AuthenticationConstants.AllowedSigningAlgorithms,
388
            this.connectorClientOptions?.proxySettings,
3!
389
        );
390

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

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

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

418
        let appId = '';
×
419

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

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

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

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

462
        return identity;
×
463
    }
464

465
    private async ChannelValidation_authenticateChannelToken(
466
        authHeader: string,
467
        serviceUrl: string,
468
        channelId: string,
469
    ): Promise<ClaimsIdentity> {
470
        const tokenValidationParameters = this.ChannelValidation_GetTokenValidationParameters();
×
471
        const tokenExtractor: JwtTokenExtractor = new JwtTokenExtractor(
×
472
            tokenValidationParameters,
473
            this.toBotFromChannelOpenIdMetadataUrl,
474
            AuthenticationConstants.AllowedSigningAlgorithms,
475
            this.connectorClientOptions?.proxySettings,
×
476
        );
477

478
        const identity: ClaimsIdentity = await tokenExtractor.getIdentityFromAuthHeader(
×
479
            authHeader,
480
            channelId,
481
            this.authConfiguration.requiredEndorsements,
482
        );
483

484
        return this.governmentChannelValidation_ValidateIdentity(identity, serviceUrl);
×
485
    }
486

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

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

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

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

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

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

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

540
        return identity;
×
541
    }
542
}
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

© 2026 Coveralls, Inc