contrition/src/entity/StateToken.ts

89 lines
2.3 KiB
TypeScript

import axios, {AxiosResponse} from "axios";
import {
BaseEntity,
Column,
Entity,
PrimaryGeneratedColumn
} from "typeorm";
import {URLSearchParams} from "url";
import {Config} from "../config/Config";
import {API_URL, OAUTH_SCOPE} from "../config/Constants";
import {DiscordToken} from "./DiscordToken";
/**
* StateToken is an OAuth2 state token.
*
* These are stored in the database and used to prevent attacks on the OAuth2
* flow, stored persistently in order of server restart, and pruned.
*
* The verify method also checks the validity of the token, and completes the OAuth2 flow.
* The constructor automatically sets the expiration time for tokens, of 30
* minutes, in case the OAuth2 flow is not completed.
*
*/
@Entity({ name: "state_tokens" })
export class StateToken extends BaseEntity {
@PrimaryGeneratedColumn("uuid")
id: string;
@Column({ unique: true })
token: string;
@Column()
expiring: Date;
constructor(token: string) {
super();
if (token === undefined) {
return;
}
this.token = token;
this.expiring = new Date();
this.expiring.setMinutes(this.expiring.getMinutes() + 30);
}
async verify({ api }: Config, code: string): Promise<DiscordToken> {
const params = new URLSearchParams();
params.append("client_id", api.client_id);
params.append("client_secret", api.client_secret);
params.append("grant_type", "authorization_code");
params.append("code", `${code}`);
params.append("redirect_uri", `${api.redirect_uri}`);
params.append("scope", OAUTH_SCOPE);
let r: AxiosResponse;
try {
r = await axios.post(`${API_URL}/oauth2/token`, params.toString(), {
validateStatus: (status) => status < 500,
});
} catch (err) {
console.error("Could not decode request data: ", this, err);
Promise.reject({
reason: "An internal error has occurred.",
status: 500,
});
return;
}
if (r.status != 200) {
console.error("Bad request from discord", r);
Promise.reject({ reason: r.data["error_description"], status: 400 });
return;
}
const authToken = r.data["access_token"];
let dToken = await DiscordToken.findOne({ where: { authToken } });
if (!dToken) {
dToken = new DiscordToken(this.token, authToken);
dToken.save();
}
this.remove();
return dToken;
}
}