Files
outline/server/utils/oauth.ts
Tom Moor 5c07694f6b Refactor 'uploadFromUrl' to base storage implementation
Add safety around using fetch implementation
2023-08-20 13:13:17 -04:00

89 lines
2.1 KiB
TypeScript

import Logger from "@server/logging/Logger";
import { AuthenticationError, InvalidRequestError } from "../errors";
import fetch from "./fetch";
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;
};
async rotateToken(
_accessToken: string,
refreshToken: string,
endpoint = this.endpoints.token
): Promise<{
accessToken: string;
refreshToken?: string;
expiresAt: Date;
}> {
let data;
let response;
try {
Logger.debug("utils", "Rotating token", { endpoint });
response = await fetch(endpoint, {
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),
};
}
}