summary refs log tree commit diff
path: root/src/util
diff options
context:
space:
mode:
authorPuyodead1 <puyodead@proton.me>2022-12-23 18:34:36 -0500
committerPuyodead1 <puyodead@proton.me>2023-03-18 19:27:39 -0400
commit0db1fa5f0b2b9b357c1f96178c0e5df7858a99ab (patch)
treeb045eee5f984a40ae47413bad2458cf65eff3c8e /src/util
parentDon't try to upload entire config for each connection loaded (diff)
downloadserver-0db1fa5f0b2b9b357c1f96178c0e5df7858a99ab.tar.xz
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)
Diffstat (limited to 'src/util')
-rw-r--r--src/util/connections/Connection.ts25
-rw-r--r--src/util/connections/ConnectionStore.ts4
-rw-r--r--src/util/connections/RefreshableConnection.ts30
-rw-r--r--src/util/connections/index.ts1
-rw-r--r--src/util/dtos/ConnectedAccountDTO.ts4
-rw-r--r--src/util/entities/ConnectedAccount.ts7
-rw-r--r--src/util/interfaces/ConnectedAccount.ts16
-rw-r--r--src/util/interfaces/index.ts5
-rw-r--r--src/util/schemas/ConnectedAccountSchema.ts4
9 files changed, 83 insertions, 13 deletions
diff --git a/src/util/connections/Connection.ts b/src/util/connections/Connection.ts

index 164cfac7..8b60b0d2 100644 --- a/src/util/connections/Connection.ts +++ b/src/util/connections/Connection.ts
@@ -1,9 +1,11 @@ import crypto from "crypto"; import { ConnectedAccount } from "../entities"; -import { OrmUtils } from "../imports"; import { ConnectedAccountSchema, ConnectionCallbackSchema } from "../schemas"; import { DiscordApiErrors } from "../util"; +/** + * A connection that can be used to connect to an external service. + */ export default abstract class Connection { id: string; settings: { enabled: boolean }; @@ -21,7 +23,9 @@ export default abstract class Connection { * Processes the callback * @param args Callback arguments */ - abstract handleCallback(params: ConnectionCallbackSchema): Promise<ConnectedAccount | null>; + abstract handleCallback( + params: ConnectionCallbackSchema, + ): Promise<ConnectedAccount | null>; /** * Gets a user id from state @@ -54,12 +58,25 @@ export default abstract class Connection { this.states.delete(state); } - async createConnection(data: ConnectedAccountSchema): Promise<ConnectedAccount> { - const ca = OrmUtils.mergeDeep(new ConnectedAccount(), data) as ConnectedAccount; + /** + * Creates a Connected Account in the database. + * @param data connected account data + * @returns the new connected account + */ + async createConnection( + data: ConnectedAccountSchema, + ): Promise<ConnectedAccount> { + const ca = ConnectedAccount.create({ ...data }); await ca.save(); return ca; } + /** + * Checks if a user has an exist connected account for the given extenal id. + * @param userId the user id + * @param externalId the connection id to find + * @returns + */ async hasConnection(userId: string, externalId: string): Promise<boolean> { const existing = await ConnectedAccount.findOne({ where: { diff --git a/src/util/connections/ConnectionStore.ts b/src/util/connections/ConnectionStore.ts
index 406e8232..759b6de7 100644 --- a/src/util/connections/ConnectionStore.ts +++ b/src/util/connections/ConnectionStore.ts
@@ -1,5 +1,7 @@ import Connection from "./Connection"; +import RefreshableConnection from "./RefreshableConnection"; export class ConnectionStore { - public static connections: Map<string, Connection> = new Map(); + public static connections: Map<string, Connection | RefreshableConnection> = + new Map(); } diff --git a/src/util/connections/RefreshableConnection.ts b/src/util/connections/RefreshableConnection.ts new file mode 100644
index 00000000..0008cbc0 --- /dev/null +++ b/src/util/connections/RefreshableConnection.ts
@@ -0,0 +1,30 @@ +import { ConnectedAccount } from "../entities"; +import { ConnectedAccountCommonOAuthTokenResponse } from "../interfaces"; +import Connection from "./Connection"; + +/** + * A connection that can refresh its token. + */ +export default abstract class RefreshableConnection extends Connection { + refreshEnabled = true; + /** + * Refreshes the token for a connected account. + * @param connectedAccount The connected account to refresh + */ + abstract refreshToken( + connectedAccount: ConnectedAccount, + ): Promise<ConnectedAccountCommonOAuthTokenResponse>; + + /** + * Refreshes the token for a connected account and saves it to the database. + * @param connectedAccount The connected account to refresh + */ + async refresh( + connectedAccount: ConnectedAccount, + ): Promise<ConnectedAccountCommonOAuthTokenResponse> { + const tokenData = await this.refreshToken(connectedAccount); + connectedAccount.token_data = tokenData; + await connectedAccount.save(); + return tokenData; + } +} diff --git a/src/util/connections/index.ts b/src/util/connections/index.ts
index e15d0c8c..8d20bf27 100644 --- a/src/util/connections/index.ts +++ b/src/util/connections/index.ts
@@ -2,3 +2,4 @@ export * from "./Connection"; export * from "./ConnectionConfig"; export * from "./ConnectionLoader"; export * from "./ConnectionStore"; +export * from "./RefreshableConnection"; diff --git a/src/util/dtos/ConnectedAccountDTO.ts b/src/util/dtos/ConnectedAccountDTO.ts
index debc5535..ca15ff41 100644 --- a/src/util/dtos/ConnectedAccountDTO.ts +++ b/src/util/dtos/ConnectedAccountDTO.ts
@@ -23,8 +23,8 @@ export class ConnectedAccountDTO { this.id = connectedAccount.external_id; this.user_id = connectedAccount.user_id; this.access_token = - connectedAccount.access_token && with_token - ? connectedAccount.access_token + connectedAccount.token_data && with_token + ? connectedAccount.token_data.access_token : undefined; this.friend_sync = connectedAccount.friend_sync; this.name = connectedAccount.name; diff --git a/src/util/entities/ConnectedAccount.ts b/src/util/entities/ConnectedAccount.ts
index 25d5a0c7..beb53e41 100644 --- a/src/util/entities/ConnectedAccount.ts +++ b/src/util/entities/ConnectedAccount.ts
@@ -17,6 +17,7 @@ */ import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; +import { ConnectedAccountTokenData } from "../interfaces"; import { BaseClass } from "./BaseClass"; import { User } from "./User"; @@ -40,9 +41,6 @@ export class ConnectedAccount extends BaseClass { }) user: User; - @Column({ select: false, nullable: true }) - access_token?: string; - @Column({ select: false }) friend_sync?: boolean = false; @@ -75,4 +73,7 @@ export class ConnectedAccount extends BaseClass { @Column() two_way_link?: boolean = false; + + @Column({ select: false, nullable: true, type: "simple-json" }) + token_data?: ConnectedAccountTokenData; } diff --git a/src/util/interfaces/ConnectedAccount.ts b/src/util/interfaces/ConnectedAccount.ts new file mode 100644
index 00000000..c96e5f79 --- /dev/null +++ b/src/util/interfaces/ConnectedAccount.ts
@@ -0,0 +1,16 @@ +export interface ConnectedAccountCommonOAuthTokenResponse { + access_token: string; + token_type: string; + scope: string; + refresh_token?: string; + expires_in?: number; +} + +export interface ConnectedAccountTokenData { + access_token: string; + token_type?: string; + scope?: string; + refresh_token?: string; + expires_in?: number; + expires_at?: number; +} diff --git a/src/util/interfaces/index.ts b/src/util/interfaces/index.ts
index fa259ce1..e194d174 100644 --- a/src/util/interfaces/index.ts +++ b/src/util/interfaces/index.ts
@@ -17,7 +17,8 @@ */ export * from "./Activity"; -export * from "./Presence"; -export * from "./Interaction"; +export * from "./ConnectedAccount"; export * from "./Event"; +export * from "./Interaction"; +export * from "./Presence"; export * from "./Status"; diff --git a/src/util/schemas/ConnectedAccountSchema.ts b/src/util/schemas/ConnectedAccountSchema.ts
index e00e4fa1..e5f838d0 100644 --- a/src/util/schemas/ConnectedAccountSchema.ts +++ b/src/util/schemas/ConnectedAccountSchema.ts
@@ -1,7 +1,9 @@ +import { ConnectedAccountTokenData } from "../interfaces"; + export interface ConnectedAccountSchema { external_id: string; user_id: string; - access_token?: string; + token_data?: ConnectedAccountTokenData; friend_sync?: boolean; name: string; revoked?: boolean;