-
Notifications
You must be signed in to change notification settings - Fork 521
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore: add token auth strategy and client credential provider
- Loading branch information
1 parent
a9826ba
commit 4916098
Showing
4 changed files
with
172 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import AuthStrategy from "./AuthStrategy"; | ||
import TokenManager from "../http/bearer_token/TokenManager"; | ||
import jwt, { JwtPayload } from "jsonwebtoken"; | ||
|
||
export default class TokenAuthStrategy extends AuthStrategy { | ||
private token: string; | ||
private tokenManager: TokenManager; | ||
|
||
constructor(tokenManager: TokenManager) { | ||
super("token"); | ||
this.token = ""; | ||
this.tokenManager = tokenManager; | ||
} | ||
|
||
async getAuthString(): Promise<string> { | ||
return this.fetchToken() | ||
.then((token) => { | ||
this.token = token; | ||
return `Bearer ${this.token}`; | ||
}) | ||
.catch((error) => { | ||
throw new Error(`Failed to fetch access token: ${error}`); | ||
}); | ||
} | ||
|
||
requiresAuthentication(): boolean { | ||
return true; | ||
} | ||
|
||
async fetchToken(): Promise<string> { | ||
if ( | ||
this.token == null || | ||
this.token.length === 0 || | ||
this.isTokenExpired(this.token) | ||
) { | ||
return this.tokenManager.fetchToken(); | ||
} | ||
return Promise.resolve(this.token); | ||
} | ||
|
||
/** | ||
* Function to check if the token is expired with a buffer of 30 seconds. | ||
* @param token - The JWT token as a string. | ||
* @returns Boolean indicating if the token is expired. | ||
*/ | ||
isTokenExpired(token: string): boolean { | ||
try { | ||
// Decode the token without verifying the signature, as we only want to read the expiration for this check | ||
const decoded = jwt.decode(token) as JwtPayload; | ||
|
||
if (!decoded || !decoded.exp) { | ||
// If the token doesn't have an expiration, consider it expired | ||
return true; | ||
} | ||
|
||
const expiresAt = decoded.exp * 1000; | ||
const bufferMilliseconds = 30 * 1000; | ||
const bufferExpiresAt = expiresAt - bufferMilliseconds; | ||
|
||
// Return true if the current time is after the expiration time with buffer | ||
return Date.now() > bufferExpiresAt; | ||
} catch (error) { | ||
// If there's an error decoding the token, consider it expired | ||
return true; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import CredentialProvider from "./CredentialProvider"; | ||
import TokenManager from "../http/bearer_token/TokenManager"; | ||
import AuthStrategy from "../auth_strategy/AuthStrategy"; | ||
import ApiTokenManager from "../http/bearer_token/ApiTokenManager"; | ||
import TokenAuthStrategy from "../auth_strategy/TokenAuthStrategy"; | ||
|
||
class ClientCredentialProvider extends CredentialProvider { | ||
grantType: string; | ||
clientId: string; | ||
clientSecret: string; | ||
tokenManager: TokenManager | null; | ||
|
||
constructor() { | ||
super("client-credentials"); | ||
this.grantType = "client_credentials"; | ||
this.clientId = ""; | ||
this.clientSecret = ""; | ||
this.tokenManager = null; | ||
} | ||
|
||
public toAuthStrategy(): AuthStrategy { | ||
if (this.tokenManager == null) { | ||
this.tokenManager = new ApiTokenManager({ | ||
grantType: this.grantType, | ||
clientId: this.clientId, | ||
clientSecret: this.clientSecret, | ||
}); | ||
} | ||
return new TokenAuthStrategy(this.tokenManager); | ||
} | ||
} | ||
|
||
namespace ClientCredentialProvider { | ||
export class ClientCredentialProviderBuilder { | ||
private readonly instance: ClientCredentialProvider; | ||
|
||
constructor() { | ||
this.instance = new ClientCredentialProvider(); | ||
} | ||
|
||
public setClientId(clientId: string): ClientCredentialProviderBuilder { | ||
this.instance.clientId = clientId; | ||
return this; | ||
} | ||
|
||
public setClientSecret( | ||
clientSecret: string | ||
): ClientCredentialProviderBuilder { | ||
this.instance.clientSecret = clientSecret; | ||
return this; | ||
} | ||
|
||
public setTokenManager( | ||
tokenManager: TokenManager | ||
): ClientCredentialProviderBuilder { | ||
this.instance.tokenManager = tokenManager; | ||
return this; | ||
} | ||
|
||
public build(): ClientCredentialProvider { | ||
return this.instance; | ||
} | ||
} | ||
} | ||
|
||
export = ClientCredentialProvider; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import TokenManager from "./TokenManager"; | ||
import { | ||
TokenListInstance, | ||
TokenListInstanceCreateOptions, | ||
} from "../../rest/previewIam/v1/token"; | ||
import PreviewIamBase from "../../rest/PreviewIamBase"; | ||
import V1 from "../../rest/previewIam/V1"; | ||
import NoAuthCredentialProvider from "../../credential_provider/NoAuthCredentialProvider"; | ||
import { Client } from "../../base/BaseTwilio"; | ||
|
||
export default class ApiTokenManager implements TokenManager { | ||
private params: TokenListInstanceCreateOptions; | ||
|
||
constructor(params: TokenListInstanceCreateOptions) { | ||
this.params = params; | ||
} | ||
|
||
async fetchToken(): Promise<string> { | ||
const noAuthCredentialProvider = | ||
new NoAuthCredentialProvider.NoAuthCredentialProvider(); | ||
const client = new Client(); | ||
client.setCredentialProvider(noAuthCredentialProvider); | ||
|
||
const tokenListInstance = TokenListInstance( | ||
new V1(new PreviewIamBase(client)) | ||
); | ||
return tokenListInstance | ||
.create(this.params) | ||
.then((token) => { | ||
return token.accessToken; | ||
}) | ||
.catch((error) => { | ||
throw new Error(`Failed to fetch access token: ${error}`); | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export default abstract class TokenManager { | ||
abstract fetchToken(): Promise<string>; | ||
} |