* chore: Store expiresAt on UserAuthentications. This represents the time that the accessToken is no longer valid and should be exchanged using the refreshToken * feat: Check and expire Google SSO * fix: Better handling of multiple auth methods Added more docs * fix: Retry access validation with network errors * Small refactor, add Azure token validation support * doc * test * lint * OIDC refresh support * CheckSSOAccessTask -> ValidateSSOAccessTask Added lastValidatedAt column Skip checks if validated within 5min Some edge cases around encrypted columns
84 lines
1.9 KiB
TypeScript
84 lines
1.9 KiB
TypeScript
import fetch from "fetch-with-proxy";
|
|
import { AuthenticationError, InvalidRequestError } from "../errors";
|
|
|
|
export default abstract class OAuthClient {
|
|
private clientId: string;
|
|
private clientSecret: string;
|
|
|
|
protected endpoints = {
|
|
authorize: "",
|
|
token: "",
|
|
userinfo: "",
|
|
};
|
|
|
|
constructor(clientId: string, clientSecret: string) {
|
|
this.clientId = clientId;
|
|
this.clientSecret = clientSecret;
|
|
}
|
|
|
|
userInfo = async (accessToken: string) => {
|
|
let data;
|
|
let response;
|
|
|
|
try {
|
|
response = await fetch(this.endpoints.userinfo, {
|
|
method: "GET",
|
|
headers: {
|
|
Authorization: `Bearer ${accessToken}`,
|
|
"Content-Type": "application/json",
|
|
},
|
|
});
|
|
data = await response.json();
|
|
} catch (err) {
|
|
throw InvalidRequestError(err.message);
|
|
}
|
|
|
|
const success = response.status >= 200 && response.status < 300;
|
|
if (!success) {
|
|
throw AuthenticationError();
|
|
}
|
|
|
|
return data;
|
|
};
|
|
|
|
rotateToken = async (
|
|
refreshToken: string
|
|
): Promise<{
|
|
accessToken: string;
|
|
refreshToken?: string;
|
|
expiresAt: Date;
|
|
}> => {
|
|
let data;
|
|
let response;
|
|
|
|
try {
|
|
response = await fetch(this.endpoints.token, {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/x-www-form-urlencoded",
|
|
},
|
|
body: new URLSearchParams({
|
|
client_id: this.clientId,
|
|
client_secret: this.clientSecret,
|
|
refresh_token: refreshToken,
|
|
grant_type: "refresh_token",
|
|
}),
|
|
});
|
|
data = await response.json();
|
|
} catch (err) {
|
|
throw InvalidRequestError(err.message);
|
|
}
|
|
|
|
const success = response.status >= 200 && response.status < 300;
|
|
if (!success) {
|
|
throw AuthenticationError();
|
|
}
|
|
|
|
return {
|
|
refreshToken: data.refresh_token,
|
|
accessToken: data.access_token,
|
|
expiresAt: new Date(Date.now() + data.expires_in * 1000),
|
|
};
|
|
};
|
|
}
|