2

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);
        });

});
Fernando Vega
  • 525
  • 4
  • 13

1 Answers1

4

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

Fjut
  • 1,314
  • 12
  • 23
  • Are you sure it's not something wrong with the chainging itself? When i execute the function the first time, it seems to jump straight down to the the"return this.token" before this.token is defined inthe previous .then statements. I've tried implementing youre method, but have not had any luck. – Fernando Vega Jan 23 '17 at 02:57
  • 1
    Yes, it 'jumps' to the return this.token line because your db.open() call is asynchronous, meaning that the result of the call will be resolved at some point in the future. During that time, the rest of the code is executed synchronously. Have a look at https://scotch.io/tutorials/javascript-promises-for-dummies and https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise, it might shed more light on the Promises. Let me know if you are still stuck afterwards. – Fjut Jan 23 '17 at 19:10
  • Thanks for the additional info. I've got something that is working, kind of... The concept is still a little fuzzy, but it's getting clearer! I have something else that's come up, but I think I'll need to post a new question for that..? – Fernando Vega Jan 24 '17 at 18:39
  • This is the new question: http://stackoverflow.com/questions/41837206/im-doing-promises-better-but-still-kind-of-wrong-one-more-thing-to-clear-up – Fernando Vega Jan 24 '17 at 19:21