Skip to content

Commit 824f21a

Browse files
[PECO-932] OAuth M2M flow: fix token refresh flow (#177)
* [PECO-932] OAuth M2M flow: fix token refresh flow Signed-off-by: Levko Kravets <levko.ne@gmail.com> * Add tests Signed-off-by: Levko Kravets <levko.ne@gmail.com> --------- Signed-off-by: Levko Kravets <levko.ne@gmail.com>
1 parent 4bf4b33 commit 824f21a

2 files changed

Lines changed: 279 additions & 144 deletions

File tree

lib/connection/auth/DatabricksOAuth/OAuthManager.ts

Lines changed: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -68,17 +68,7 @@ export default abstract class OAuthManager {
6868
return this.client;
6969
}
7070

71-
public async refreshAccessToken(token: OAuthToken): Promise<OAuthToken> {
72-
try {
73-
if (!token.hasExpired) {
74-
// The access token is fine. Just return it.
75-
return token;
76-
}
77-
} catch (error) {
78-
this.logger?.log(LogLevel.error, `${error}`);
79-
throw error;
80-
}
81-
71+
private async refreshAccessTokenU2M(token: OAuthToken): Promise<OAuthToken> {
8272
if (!token.refreshToken) {
8373
const message = `OAuth access token expired on ${token.expirationTime}.`;
8474
this.logger?.log(LogLevel.error, message);
@@ -99,7 +89,33 @@ export default abstract class OAuthManager {
9989
return new OAuthToken(accessToken, refreshToken);
10090
}
10191

102-
private async getTokenU2M(client: BaseClient, scopes: OAuthScopes) {
92+
private async refreshAccessTokenM2M(): Promise<OAuthToken> {
93+
const { access_token: accessToken, refresh_token: refreshToken } = await this.getTokenM2M();
94+
95+
if (!accessToken) {
96+
throw new Error('Failed to fetch access token');
97+
}
98+
99+
return new OAuthToken(accessToken, refreshToken);
100+
}
101+
102+
public async refreshAccessToken(token: OAuthToken): Promise<OAuthToken> {
103+
try {
104+
if (!token.hasExpired) {
105+
// The access token is fine. Just return it.
106+
return token;
107+
}
108+
} catch (error) {
109+
this.logger?.log(LogLevel.error, `${error}`);
110+
throw error;
111+
}
112+
113+
return this.options.clientSecret === undefined ? this.refreshAccessTokenU2M(token) : this.refreshAccessTokenM2M();
114+
}
115+
116+
private async getTokenU2M(scopes: OAuthScopes) {
117+
const client = await this.getClient();
118+
103119
const authCode = new AuthorizationCode({
104120
client,
105121
ports: this.getCallbackPorts(),
@@ -118,18 +134,23 @@ export default abstract class OAuthManager {
118134
});
119135
}
120136

121-
public async getTokenM2M(client: BaseClient) {
122-
return client.grant({
137+
private async getTokenM2M() {
138+
const client = await this.getClient();
139+
140+
// M2M flow doesn't really support token refreshing, and refresh should not be available
141+
// in response. Each time access token expires, client can just acquire a new one using
142+
// client secret. Here we explicitly return access token only as a sign that we're not going
143+
// to use refresh token for M2M flow anywhere later
144+
const { access_token: accessToken } = await client.grant({
123145
grant_type: 'client_credentials',
124146
scope: 'all-apis', // this is the only allowed scope for M2M flow
125147
});
148+
return { access_token: accessToken, refresh_token: undefined };
126149
}
127150

128151
public async getToken(scopes: OAuthScopes): Promise<OAuthToken> {
129-
const client = await this.getClient();
130-
131152
const { access_token: accessToken, refresh_token: refreshToken } =
132-
this.options.clientSecret === undefined ? await this.getTokenU2M(client, scopes) : await this.getTokenM2M(client);
153+
this.options.clientSecret === undefined ? await this.getTokenU2M(scopes) : await this.getTokenM2M();
133154

134155
if (!accessToken) {
135156
throw new Error('Failed to fetch access token');

0 commit comments

Comments
 (0)