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

microsoft / botbuilder-js / 7249218471

18 Dec 2023 02:08PM UTC coverage: 84.637% (+0.008%) from 84.629%
7249218471

Pull #4588

github

web-flow
Merge c33ec8a2c into f3db3e98b
Pull Request #4588: fix: USGovSingleTenant OAuthEndpoint

9978 of 13059 branches covered (0.0%)

Branch coverage included in aggregate %.

27 of 28 new or added lines in 8 files covered. (96.43%)

40 existing lines in 2 files now uncovered.

20383 of 22813 relevant lines covered (89.35%)

7190.88 hits per line

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

93.1
/libraries/botframework-connector/src/auth/appCredentials.ts
1
/**
2
 * @module botframework-connector
3
 */
4
/**
5
 * Copyright (c) Microsoft Corporation. All rights reserved.
6
 * Licensed under the MIT License.
7
 */
8

9
import { ConfidentialClientApplication } from '@azure/msal-node';
10
import { ServiceClientCredentials, WebResource } from '@azure/core-http';
11
import { TokenCredentials } from './tokenCredentials';
2✔
12
import { AuthenticationConstants } from './authenticationConstants';
2✔
13
import { AuthenticatorResult } from './authenticatorResult';
14

15
/**
16
 * General AppCredentials auth implementation and cache.
17
 * Subclasses can implement refreshToken to acquire the token.
18
 */
19
export abstract class AppCredentials implements ServiceClientCredentials {
2✔
20
    private static readonly cache: Map<string, AuthenticatorResult> = new Map<string, AuthenticatorResult>();
2✔
21

22
    appId: string;
23

24
    private _oAuthEndpoint: string;
25
    private _oAuthScope: string;
26
    private _tenant: string;
27
    tokenCacheKey: string;
28
    protected clientApplication: ConfidentialClientApplication;
29

30
    // Protects against JSON.stringify leaking secrets
31
    private toJSON(): unknown {
32
        return {
×
33
            name: this.constructor.name,
34
            appId: this.appId,
35
            tenant: this.tenant,
36
            oAuthEndpoint: this.oAuthEndpoint,
37
            oAuthScope: this.oAuthScope,
38
        };
39
    }
40

41
    /**
42
     * Initializes a new instance of the [AppCredentials](xref:botframework-connector.AppCredentials) class.
43
     *
44
     * @param appId The App ID.
45
     * @param channelAuthTenant Optional. The oauth token tenant.
46
     * @param oAuthScope The scope for the token.
47
     */
48
    constructor(
49
        appId: string,
50
        channelAuthTenant?: string,
51
        oAuthScope: string = null
680✔
52
    ) {
53
        this.appId = appId;
740✔
54
        this.tenant = channelAuthTenant;
740✔
55
        this.oAuthEndpoint = this.GetToChannelFromBotLoginUrlPrefix() + this.tenant;
740✔
56
        this.oAuthScope = (oAuthScope && oAuthScope.length > 0)
740✔
57
            ? oAuthScope
740✔
58
            : this.GetToChannelFromBotOAuthScope();
59
    }
60

61
    /**
62
     * Gets tenant to be used for channel authentication.
63
     *
64
     * @returns The channel auth token tenant for this credential.
65
     */
66
    private get tenant(): string {
67
        return this._tenant;
740✔
68
    }
69

70
    /**
71
     * Sets tenant to be used for channel authentication.
72
     */
73
    private set tenant(value: string) {
74
        this._tenant = value && value.length > 0 ? value : this.GetDefaultChannelAuthTenant();
740✔
75
    }
76

77
    /**
78
     * Gets the OAuth scope to use.
79
     *
80
     * @returns The OAuth scope to use.
81
     */
82
    get oAuthScope(): string {
83
        return this._oAuthScope;
972✔
84
    }
85

86
    /**
87
     * Sets the OAuth scope to use.
88
     */
89
    set oAuthScope(value: string) {
90
        this._oAuthScope = value;
744✔
91
        this.tokenCacheKey = `${this.appId}${this.oAuthScope}-cache`;
744✔
92
    }
93

94
    /**
95
     * Gets the OAuth endpoint to use.
96
     *
97
     * @returns The OAuthEndpoint to use.
98
     */
99
    get oAuthEndpoint(): string {
100
        return this._oAuthEndpoint;
26✔
101
    }
102

103
    /**
104
     * Sets the OAuth endpoint to use.
105
     */
106
    set oAuthEndpoint(value: string) {
107
        // aadApiVersion is set to '1.5' to avoid the "spn:" concatenation on the audience claim
108
        // For more info, see https://github.com/AzureAD/azure-activedirectory-library-for-nodejs/issues/128
109
        this._oAuthEndpoint = value;
738✔
110
    }
111

112
    /**
113
     * Adds the host of service url to trusted hosts.
114
     * If expiration time is not provided, the expiration date will be current (utc) date + 1 day.
115
     *
116
     * @deprecated
117
     *
118
     * @param  {string} serviceUrl The service url
119
     * @param  {Date} expiration? The expiration date after which this service url is not trusted anymore
120
     */
121
    static trustServiceUrl(serviceUrl: string, expiration?: Date): void;
122
    /**
123
     * Adds the host of service url to trusted hosts.
124
     * If expiration time is not provided, the expiration date will be current (utc) date + 1 day.
125
     */
126
    static trustServiceUrl(): void {
127
        // no-op
128
    }
129

130
    /**
131
     * Checks if the service url is for a trusted host or not.
132
     *
133
     * @deprecated
134
     *
135
     * @param  {string} serviceUrl The service url
136
     * @returns {boolean} True if the host of the service url is trusted; False otherwise.
137
     */
138
    static isTrustedServiceUrl(serviceUrl: string): boolean;
139
    /**
140
     * Checks if the service url is for a trusted host or not.
141
     *
142
     * @returns {boolean} True if the host of the service url is trusted; False otherwise.
143
     */
144
    static isTrustedServiceUrl(): boolean {
UNCOV
145
        return true;
×
146
    }
147

148
    /**
149
     * Apply the credentials to the HTTP request.
150
     *
151
     * @param webResource The WebResource HTTP request.
152
     * @returns A Promise representing the asynchronous operation.
153
     */
154
    async signRequest(webResource: WebResource): Promise<WebResource> {
155
        if (this.shouldSetToken()) {
208✔
156
            return new TokenCredentials(await this.getToken()).signRequest(webResource);
76✔
157
        }
158

159
        return webResource;
132✔
160
    }
161

162
    /**
163
     * Gets an OAuth access token.
164
     *
165
     * @param forceRefresh True to force a refresh of the token; or false to get
166
     * a cached token if it exists.
167
     * @returns A Promise that represents the work queued to execute.
168
     * @remarks If the promise is successful, the result contains the access token string.
169
     */
170
    async getToken(forceRefresh = false): Promise<string> {
80✔
171
        if (!forceRefresh) {
126✔
172
            // check the global cache for the token. If we have it, and it's valid, we're done.
173
            const oAuthToken = AppCredentials.cache.get(this.tokenCacheKey);
80✔
174
            // Check if the token is not expired.
175
            if (oAuthToken && oAuthToken.expiresOn > new Date()) {
80✔
176
                return oAuthToken.accessToken;
2✔
177
            }
178
        }
179

180
        // We need to refresh the token, because:
181
        // 1. The user requested it via the forceRefresh parameter
182
        // 2. We have it, but it's expired
183
        // 3. We don't have it in the cache.
184
        const res = await this.refreshToken();
124✔
185

186
        if (res && res.accessToken) {
124!
187
            // Subtract 5 minutes from expiresOn so they'll we'll get a new token before it expires.
188
            res.expiresOn.setMinutes(res.expiresOn.getMinutes() - 5);
124✔
189
            AppCredentials.cache.set(this.tokenCacheKey, res);
124✔
190
            return res.accessToken;
124✔
191
        } else {
192
            throw new Error('Authentication: No response or error received from MSAL.');
×
193
        }
194
    }
195

196
    protected GetToChannelFromBotOAuthScope(): string {
197
        return AuthenticationConstants.ToChannelFromBotOAuthScope;
672✔
198
    }
199

200
    protected GetToChannelFromBotLoginUrlPrefix(): string {
201
        return AuthenticationConstants.ToChannelFromBotLoginUrlPrefix;
724✔
202
    }
203

204
    protected GetDefaultChannelAuthTenant(): string {
205
        return AuthenticationConstants.DefaultChannelAuthTenant;
718✔
206
    }
207

208
    protected abstract refreshToken(): Promise<AuthenticatorResult>;
209

210
    /**
211
     * @private
212
     */
213
    private shouldSetToken(): boolean {
214
        return this.appId && this.appId !== AuthenticationConstants.AnonymousSkillAppId;
208✔
215
    }
216
}
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