mirror of
https://github.com/spacebarchat/server.git
synced 2026-08-27 22:34:11 +00:00
Refreshable connections, refactoring, access-token endpoint
- Aded /users/@me/connections/:connection_name/:connection_id/access-token - Replaced `access_token` property on ConnectedAccount with `token_data` object for refreshing tokens - Made a common interface for connection things like ComonOAuthTokenResponse - Added `RefreshableConnection` class - Added token refresh to Spotify connection (disabled)
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
import { route } from "@fosscord/api";
|
||||
import {
|
||||
ApiError,
|
||||
ConnectedAccount,
|
||||
ConnectionStore,
|
||||
DiscordApiErrors,
|
||||
FieldErrors,
|
||||
} from "@fosscord/util";
|
||||
import { Request, Response, Router } from "express";
|
||||
import RefreshableConnection from "../../../../../../../util/connections/RefreshableConnection";
|
||||
const router = Router();
|
||||
|
||||
// TODO: this route is only used for spotify, twitch, and youtube. (battlenet seems to be able to PUT, maybe others also)
|
||||
|
||||
// spotify is disabled here because it cant be used
|
||||
const ALLOWED_CONNECTIONS = ["twitch", "youtube"];
|
||||
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
// TODO: get the current access token or refresh it if it's expired
|
||||
const { connection_name, connection_id } = req.params;
|
||||
|
||||
const connection = ConnectionStore.connections.get(connection_id);
|
||||
|
||||
if (!ALLOWED_CONNECTIONS.includes(connection_name) || !connection)
|
||||
throw FieldErrors({
|
||||
provider_id: {
|
||||
code: "BASE_TYPE_CHOICES",
|
||||
message: req.t("common:field.BASE_TYPE_CHOICES", {
|
||||
types: ALLOWED_CONNECTIONS.join(", "),
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
if (!connection.settings.enabled)
|
||||
throw FieldErrors({
|
||||
provider_id: {
|
||||
message: "This connection has been disabled server-side.",
|
||||
},
|
||||
});
|
||||
|
||||
const connectedAccount = await ConnectedAccount.findOne({
|
||||
where: {
|
||||
type: connection_name,
|
||||
id: connection_id,
|
||||
user_id: req.user_id,
|
||||
},
|
||||
select: [
|
||||
"external_id",
|
||||
"type",
|
||||
"name",
|
||||
"verified",
|
||||
"visibility",
|
||||
"show_activity",
|
||||
"revoked",
|
||||
"token_data",
|
||||
"friend_sync",
|
||||
"integrations",
|
||||
],
|
||||
});
|
||||
if (!connectedAccount) throw DiscordApiErrors.UNKNOWN_CONNECTION;
|
||||
if (connectedAccount.revoked)
|
||||
throw new ApiError("Connection revoked", 0, 400);
|
||||
if (!connectedAccount.token_data)
|
||||
throw new ApiError("No token data", 0, 400);
|
||||
|
||||
let access_token = connectedAccount.token_data.access_token;
|
||||
const { expires_at, expires_in } = connectedAccount.token_data;
|
||||
|
||||
if (expires_at && expires_at < Date.now()) {
|
||||
if (!(connection instanceof RefreshableConnection))
|
||||
throw new ApiError("Access token expired", 0, 400);
|
||||
const tokenData = await connection.refresh(connectedAccount);
|
||||
access_token = tokenData.access_token;
|
||||
} else if (expires_in && expires_in < Date.now()) {
|
||||
if (!(connection instanceof RefreshableConnection))
|
||||
throw new ApiError("Access token expired", 0, 400);
|
||||
const tokenData = await connection.refresh(connectedAccount);
|
||||
access_token = tokenData.access_token;
|
||||
}
|
||||
|
||||
res.json({ access_token });
|
||||
});
|
||||
|
||||
export default router;
|
||||
@@ -35,7 +35,7 @@ router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
"visibility",
|
||||
"show_activity",
|
||||
"revoked",
|
||||
"access_token",
|
||||
"token_data",
|
||||
"friend_sync",
|
||||
"integrations",
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user