Skip to content

Commit

Permalink
Merge pull request #485 from curveball/client-set-one-time-token
Browse files Browse the repository at this point in the history
Allow client to set expiry time of one-time tokens
  • Loading branch information
evert committed Dec 1, 2023
2 parents 69a83a6 + af789e1 commit cab0a5f
Show file tree
Hide file tree
Showing 5 changed files with 38 additions and 9 deletions.
6 changes: 6 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
Changelog
=========

0.25.1 (????-??-??)
-------------------

* Clients can now specify how long a one-time-token should be valid for.


0.25.0 (2023-11-22)
-------------------

Expand Down
15 changes: 15 additions & 0 deletions schemas/one-time-token.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"$schema": "https://json-schema.org/draft/2019-09/schema",
"$id": "https://curveballjs.org/schemas/a12nserver/one-time-token-generate.json",
"type": "object",
"description": "The request made to the one-time-token generate endpoint.",
"required": [],
"additionalProperties": false,

"properties": {
"expiresIn": {
"description": "Specify how long the token is valid for, in seconds.",
"type": "number"
}
}
}
10 changes: 9 additions & 1 deletion src/one-time-token/controller/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,24 @@ import { createToken } from '../service';
import * as hal from '../formats/hal';
import { resolve } from 'url';

type GenerateRequest = {
expiresIn?: number;
}

class OneTimeTokenController extends Controller {

async post(ctx: Context<any>) {

ctx.request.validate<GenerateRequest>('https://curveballjs.org/schemas/a12nserver/one-time-token-generate.json');
ctx.privileges.require('a12n:one-time-token:generate');

const principalService = new PrincipalService(ctx.privileges);
const user = await principalService.findByExternalId(ctx.params.id, 'user');

const token = await createToken(user);
const token = await createToken(
user,
ctx.request.body.expiresIn ?? null,
);
const url = resolve(ctx.request.origin, 'reset-password/token/' + token.token);

ctx.response.body = hal.oneTimeToken(user, url, token);
Expand Down
14 changes: 7 additions & 7 deletions src/one-time-token/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,16 @@ const tokenTTL = 7200;
/**
* This function will create a unique token then store it in the database
*/
export async function createToken(user: User): Promise<OneTimeToken> {
export async function createToken(user: User, expiresIn: number | null): Promise<OneTimeToken> {
const token = await generateSecretToken();
const query = 'INSERT INTO reset_password_token (user_id, token, expires_at, created_at) VALUES (?, ?, ?, ?)';
const expiresAt = Math.floor(Date.now() / 1000) + (expiresIn ?? tokenTTL);

await db.raw(query, [
user.id,
await db('reset_password_token').insert({
user_id: user.id,
token,
Math.floor(Date.now() / 1000) + tokenTTL,
Math.floor(Date.now() / 1000),
]);
expires_at: expiresAt,
created_at: Math.floor(Date.now() / 1000)
});
return {
token,
expires: new Date(Date.now() + tokenTTL*1000),
Expand Down
2 changes: 1 addition & 1 deletion src/reset-password/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export async function sendResetPasswordEmail(user: User) {
const smtpUrl = requireSetting('smtp.url')!;

const transporter = nodemailer.createTransport(smtpUrl);
const token = await createToken(user);
const token = await createToken(user, null);
const emailTemplate = render('emails/reset-password-email', {
name: user.nickname,
url: process.env.PUBLIC_URI + 'reset-password/token/' + token.token,
Expand Down

0 comments on commit cab0a5f

Please sign in to comment.