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

microsoft / botbuilder-js / 16948256108

13 Aug 2025 08:14PM UTC coverage: 84.361% (-0.1%) from 84.47%
16948256108

push

github

web-flow
bump: dependencies to safe versions (#4896)

* Bump dependencies to safe versions

* Add flag to avoid test failing in Node > 22.18

* Add flag to avoid test failing in Node > 22.18 to test:min

8282 of 10996 branches covered (75.32%)

Branch coverage included in aggregate %.

20589 of 23227 relevant lines covered (88.64%)

3863.72 hits per line

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

27.43
/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> {
88
        if (await this.credentialsFactory.isAuthenticationDisabled()) {
×
89
            return SkillValidation.createAnonymousSkillClaim();
×
90
        } else {
91
            if (!authHeader.trim()) {
×
92
                throw new AuthenticationError(
×
93
                    'Unauthorized Access. Request is not authorized',
94
                    StatusCodes.UNAUTHORIZED,
95
                );
96
            }
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!
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

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
            this.connectorClientOptions?.tokenRefreshInterval,
×
303
        );
304

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

313
        await this.SkillValidation_ValidateIdentity(identity);
×
314

315
        return identity;
×
316
    }
317

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

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

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

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

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

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

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

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

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

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

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

420
        let appId = '';
×
421

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

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

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

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

464
        return identity;
×
465
    }
466

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

481
        const identity: ClaimsIdentity = await tokenExtractor.getIdentityFromAuthHeader(
×
482
            authHeader,
483
            channelId,
484
            this.authConfiguration.requiredEndorsements,
485
        );
486

487
        return this.governmentChannelValidation_ValidateIdentity(identity, serviceUrl);
×
488
    }
489

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

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

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

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

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

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

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

543
        return identity;
×
544
    }
545
}
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