Search code examples
typescriptpromisejwtaureliaes6-promise

I'm doing Promises wrong... What am I missing here?


I have a file token.ts that exports 1 function:

import * as jwt from 'jsonwebtoken';
import { db, dbUserLevel } from '../util/db';


export function genToken(username, password): Object {

    let token: Object;

    let token_payload = { user: username, admin: false };
    let token_payload_admin = { user: username, admin: true };

    // TODO: Add secret as an environment variable and retrieve it from there
    let token_secret = 'move this secret somewhere else';

    let token_header = {
        issuer: 'SomeIssuer',
        algorithm: 'HS256',
        expiresIn: '1h'
    };

    db.open().then(() => { dbUserLevel('admin') }).then(() => {
        db.collection('users').findOne({ username: username, password: password })
            .then((result) => {
                if (result.isAdmin === 1) {
                    this.token = { access_token: jwt.sign(token_payload_admin, token_secret, token_header) }
                } else if (result.isAdmin === 0) {
                    this.token = { access_token: jwt.sign(token_payload, token_secret, token_header) }
                }
                db.close();
            })
    })
    return this.token;
};

I have another file login.ts which imports the genToken function from the token.ts file:

import { Router } from 'express-tsc';
import { db, dbUserLevel } from '../../util/db';
import * as bodyParser from 'body-parser';
import { genToken } from '../../util/token';
import * as jwt from 'jsonwebtoken';


export var router = Router();

let urlencodedParser = bodyParser.urlencoded({ extended: false });
let jsonParser = bodyParser.json();


router.post('/', jsonParser, (req, res) => {

    req.accepts(['json', 'text/plain']);
    let data = req.body;
    console.log(data);

    let username: string = data["username"];
    let password: string = data["password"];

    let token = genToken(username, password);

    console.log(JSON.stringify(token));
    res.send(200, token);



});

What is supposed to happen is when I submit the form from my login.ts file, it will post the response to server and call that genToken(username, password) function and return the token Object.

For some reason, not sure why, when I submit the form the first time, "token" (login.ts) is undefined. If I submit the form again, then token Object is returned and logged to console as expected....

Anyone know why this might be? If I have not included enough information, please let me know what you might need so I can update the post! Thanks!

EDIT

Based on the info provided in the accepted answer, I came up with the following changes that have addressed my initial problem:

token.ts :

...

    let token: Object;

    let query = db.open()
        .then(() => dbUserLevel('user'))
        .then(() => db.collection('users').findOne({ username: username, password: password })
            .then((result) => {
                if (result.isAdmin === 1) {
                    token = { access_token: jwt.sign(token_payload_admin, token_secret, token_header) }
                } else if (result.isAdmin === 0) {
                    token = { access_token: jwt.sign(token_payload, token_secret, token_header) }
                }
            })
            .catch(err => {
                db.close();
                Promise.reject(err);
            }))
        .then(() => {
            db.close();
            Promise.resolve(token);
            return token;
        })
        .catch(err => {
            db.close();
            Promise.reject(err);
            return err;
        });

    return query;
};

login.ts :

...
    genToken(username, password)
        .then(token => {
            res.status(200).send(token);
        })
        .catch(err => {
            res.status(500).send(err);
        });

});

Solution

  • As far as I can see, the issue that you are experiencing is that the token is generated in an async manner while you are trying to read it in a sync way.

    Your genToken method should return a Promise and you should send your request once that promise is resolved. Something like:

    getToken(...).then((token) => {
        res.send(200, token);
    }
    

    You can find some examples here