-
Notifications
You must be signed in to change notification settings - Fork 517
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore: add support for oauth in public apis (#1050)
- Loading branch information
1 parent
b463f4e
commit 1299c58
Showing
26 changed files
with
3,019 additions
and
20 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,11 @@ | ||
export default abstract class AuthStrategy { | ||
private authType: string; | ||
protected constructor(authType: string) { | ||
this.authType = authType; | ||
} | ||
getAuthType(): string { | ||
return this.authType; | ||
} | ||
abstract getAuthString(): Promise<string>; | ||
abstract requiresAuthentication(): boolean; | ||
} |
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,23 @@ | ||
import AuthStrategy from "./AuthStrategy"; | ||
|
||
export default class BasicAuthStrategy extends AuthStrategy { | ||
private username: string; | ||
private password: string; | ||
|
||
constructor(username: string, password: string) { | ||
super("basic"); | ||
this.username = username; | ||
this.password = password; | ||
} | ||
|
||
getAuthString(): Promise<string> { | ||
const auth = Buffer.from(this.username + ":" + this.password).toString( | ||
"base64" | ||
); | ||
return Promise.resolve(`Basic ${auth}`); | ||
} | ||
|
||
requiresAuthentication(): boolean { | ||
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,15 @@ | ||
import AuthStrategy from "./AuthStrategy"; | ||
|
||
export default class NoAuthStrategy extends AuthStrategy { | ||
constructor() { | ||
super("noauth"); | ||
} | ||
|
||
getAuthString(): Promise<string> { | ||
return Promise.resolve(""); | ||
} | ||
|
||
requiresAuthentication(): boolean { | ||
return false; | ||
} | ||
} |
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.message}`); | ||
}); | ||
} | ||
|
||
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
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
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
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; |
Oops, something went wrong.