Can I authorize users in my application (JWT) without front-end? - javascript

So, I am new at node and decided to create a user authentication/authorization system. Btw, I am using ejs as view engine and not any front-end framework. Everything works when I use postman to check API. Users are registered, tokens are generated while logging in and most importantly authorization also works when I manually put generated token in header (using postman). But how could I extract those tokens and put them in headers without postman? I saw some implementations using axios, fetch etc. But all of them were using front-end frameworks to do that.
Logging in users
Accessing protected route without token
Accessing protected route with token
UPDATE:
So the problem was in passport.authenticate() method and I added my personal verification method to extract cookies specifically.
const verifyJWT = (req: Request, res: Response, next: NextFunction) => {
const signedToken = req.cookies.jwt;
if (signedToken) {
jwt.verify(
signedToken,
config.PRIV_KEY,
{ algorithms: ["RS256"] },
(err: any, decodedToken: any) => {
if (err) {
// tslint:disable-next-line:no-console
console.log(err);
} else {
// tslint:disable-next-line:no-console
console.log(decodedToken);
next();
}
}
);
} else {
res.send("Unathorized");
}
};

You don't have to use frameworks but you do need to use javascript. Whatever you do with postman you can do using fetch or axios, you can save the tokens in your cookie.

Related

Using Outlook REST API Beta From an Outlook add-in

I have created an outlook add-in with ReactJS and followed this guide to get a token to be able to use the Outlook v2.0 REST APIs: https://learn.microsoft.com/en-us/office/dev/add-ins/outlook/use-rest-api
Now I would like to start using the Outlook Beta REST APIs and I figured I could use the same token to make the API calls, however I get the following error which suggests I cannot use this token:
{"error":{"code":"UnableToReadToken","message":"OAuth token submitted with the request can not be parsed.","innerError":{"requestId":"b96fc800-82d4-4b6d-8aa0-0b9ff6a36873","date":"2020-02-21T09:27:27"}}}
Is there anyway to call this API by using the token generated by Office.context.mailbox.getCallbackTokenAsync? I am aware that I can probably get an oauth2 token via Azure AD, however within the Azure AD Portal I do not have the proper admin access to follow this process so I am looking for a solution which does not rely on that.
Here is a code snippet of my functions to get the token and call the API:
getToken() {
return new Promise(async function (resolve, reject) {
try {
Office.context.mailbox.getCallbackTokenAsync({ isRest: true }, function (result) {
if (result.status === "succeeded") {
let accessToken = result.value;
console.log(result.value);
resolve(accessToken);
} else {
console.log(result.status);
reject(result.status);
}
});
} catch (error) {
console.error(error);
reject(error);
}
})
}
getRules(token) {
return new Promise(async function (resolve, reject) {
try {
const url = 'https://outlook.office.com/api/beta/me/mailfolders/inbox/messagerules';
const header = new Headers({ 'Authorization': `Bearer ${token}` });
const options = {
headers: header
};
let response = await fetch(url, options);
let jsonResponse = await response.json();
console.log(jsonResponse);
resolve(jsonResponse);
} catch (error) {
console.error(error);
reject(error);
}
});
}
You mention not having the proper admin access to use the AD v2 authentication endpoint.
There are currently two approaches to handle app registration and user authorization. Did you confirm, if by chance one of these methods might still work...
Use Azure AD v2 authentication endpoint:
https://learn.microsoft.com/en-us/previous-versions/office/office-365-api/api/beta/use-outlook-rest-api-beta#RegAuthConverged
Use Azure Active Directory and OAuth:
https://learn.microsoft.com/en-us/previous-versions/office/office-365-api/api/beta/use-outlook-rest-api-beta#RegAuthAzure
...
Some additional information (which you might already be aware of):
The v2 authentication endpoint has been promoted from preview to Generally Available (GA) status for Outlook and Outlook.com developers.
If you have an in-production app that uses Windows Live API to access Outlook.com mailbox data, you must rewrite the app to use the the v2 authentication endpoint and the Outlook REST API. Because Windows Live API is being deprecated for Outlook.com, and Outlook.com users get their mailboxes enabled for the Outlook REST API, these users will get HTTP 404 errors when attempting to run such Windows Live API apps.
Read more here: https://learn.microsoft.com/en-us/previous-versions/office/office-365-api/api/beta/use-outlook-rest-api-beta

How to get JWT token decoded from get request in node api

I'm sending JWT tokens accross requests for authorization, however I can't seem to get the token decode each time. It works with one method but not the other. The first snippet gives a "decoded" token result from the server side, however the second one doesn't.
public async getAllUsers(req: Request, res: Response) {
try {
const payload = req["decoded"]; // gives the token decoded
if (payload) {
let users: ILoginResult = await UserData.getAllUsers(payload);
res.status(users.status).send(users.result);
}
} catch (e) {
res.status(500).send({ error: e.toString() });
}
}
public async getAccountDetails(req: Request, res: Response) {
try {
const user = req["decoded"]; // always undefined
let details: IDetails = await AccountData.getAccountDetails(name);
res.status(200).send(details);
} catch (e) {
let err = e.toString();
res.status(500).send({ error: err });
}
}
The request from postman are included a bearer token which is provided at login and used throughout other parts of the app. Not sure why it works in the one but not the other. Would really appreciate if someone could better explain what's going on here and/or provide tips, advice, suggestions.
edit - adding request details
get request to: http://localhost:5000/api/v1/account
with a token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYWRtaW4iLCJpYXQiOjE1Nzc5OTUwMjUsImV4cCI6MTU3ODE2NzgyNSwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdCJ9.--msLba1VPs4Nv_B9YL6fk2DFHkQCgiVvDJFPt_UnDk
The decoded property was used in a tutorial I was following that seemed to be added from the server side but was poorly explained and I haven't found a good alternative/explanation. I don't think it has any middleware either. Very much open to alt methods.
Thanks to the suggestions from the comments I was able to find a missing piece in the route that creates the decoded property which is being used here. By adding the middleware to the router the request works as expected:
import express from "express";
import UserController from "../controllers/UserController";
import valid from "../utils/ValidateToken";
export default (router: express.Router) => {
router
.route("/users")
.post(UserController.addUser)
.get(valid.validateToken, UserController.getAllUsers);
router.route("/login").post(UserController.loginUser);
router.route("/account").get(valid.validateToken, UserController.getAccountDetails);
};
The valid.validateToken was missing which is the bit that generates the decoded object from the JWT being passed. Moral of the story, always double check everything. Thanks to all who commented/answered!

How it is supposed to auth requests from SSR to Feathers JS API?

There is examples about how to access FeathersJS API from SSR, but they lack any info on how it is supposed to authorize such requests.
Is it ok to instantiate feathers-client app for every request? Would not it be to heavy?
There is an official example of how to call feathers API from server side:
// Set up a socket connection to our remote API
const socket = io('http://api.feathersjs.com');
const api = client().configure(socketio(socket));
​
app.get('/messages', function(req, res, next){
api.service('messages')
.find({ query: {$sort: { updatedAt: -1 } } })
.then(result => res.render('message-list', result.data))
.catch(next);
});
But what if the messages service will require authenticated user?
Should i just manually get token from SSR's req and add it somehow to api instance or api.service call?
Taking in mind the asynchronous nature of node it seems that durable way here is to call client() inside the app.get '/messages' handler, is it a supposed way?
It is also unclear does one of the Feathers boilerplate examples have durable SSR authentication, i've described it here.
Here is how i got it working.
On every request SSR create an API adapter before routing:
app.use('/', (req, res, next) => {
req.api = APIClient(req);
next();
});
APIClient constructor gets token from cookie an sets it using the set('accessToken', token) method, provided by feathers-authentication-client plugin:
'use strict';
const feathers = require('feathers');
const superagent = require('superagent');
const hooks = require('feathers-hooks')
const feathers_rest = require('feathers-rest/client');
const auth_plugin = require('feathers-authentication-client');
const config = require('../config');
const host = clientUrl => (
__SERVER__ ? `http://${config.apiHost}:${config.apiPort}` : clientUrl
);
/* API adaptor constructor.
*/
module.exports = function APIClient(req) {
const api = feathers()
// REST plugin gives ability to query services over HTTP,
// superagent used as an isomorphic HTTPClient.
.configure(feathers_rest(host('/api')).superagent(superagent))
.configure(hooks())
// Auth plugin gives ability to set accessToken
.configure(auth_plugin())
;
if (__SERVER__) {
api.set('accessToken', req['cookies']['feathers-jwt']);
}
return api;
}
So, here is a page loading flow i've got:
When one type 'my-app.com' in a browser, it sends a GET request to SSR, passing an access token in feathers-jwt cookie.
SSR creates a feathers client, fetches access token from the cookie and gives it to the client by api.set('accessToken', token) method.
SSR gets data from API using this client and gives it to the template engine (pug/react etc).
SSR returns rendered page to the browser.
Also one need to set token in browser when making requests to API, because if it is on another domain there will be no cookie, and it is better to use Authorization header or token parameter when accessing API.
Discussion link.

How to pass Request cookies through node-fetch in isomorphic app?

I'm trying to build isomorphic project using React, Express and isomorphic fetch (based on whatwg-fetch on client and node-fetch on server), from this common boilerplate. I'm using cookies for my access token, and credentials: 'same-origin' on front-end side to send it to GraphQL -- works pretty well.
The problem is that I can't use the same solution for server side -- node-fetch just don't support using of XMLHttpRequest cookies from the box. My fetch request is under few abstract layers from router, so I can't just use cookie value from req.
Here is my server.js code (full version):
server.get('*', async (req, res, next) => {
try {
// some presettings here..
await Router.dispatch({ path: req.path, query: req.query, context }, (state, component) => {
data.body = ReactDOM.renderToString(component);
});
res.send(template(data));
} catch (err) {
next(err);
}
});
and Route's index.js (full version):
export const action = async (state) => {
const response = await fetch('/graphql?query={me{id,email}}', {
credentials: 'same-origin',
});
const { data } = await response.json();
// ...
return <Login title={title} me={data.me} />;
};
How can I pass my token from server.js to my fetch module? Or, maybe there are some better decisions?
First off, I hope you have found an answer by now!
Secondly, cookies are really just headers. If you need to send a cookie to authorize server-side requests, you can always just create the string that you need for the cookie value and send it as a header.
For an example, take a look at how this server-side node-fetch wrapper appends saved cookies to the outbound request: https://github.com/valeriangalliat/fetch-cookie/blob/master/index.js#L17

Protecting against CSRF attacks in Aurelia

In Aurelia, there doesn't seem to be any support for CSRF protection yet, as opposed to AngularJS's XSRF-TOKEN header which is set automatically on all XHR requests by the AngularJS framework.
How should I go about protecting an Aurelia app from CSRF attacks? Should I roll my own support based on the OWASP CSRF Prevention Cheat Sheet, or are there any alternatives out there for Aurelia already?
You should be able to do this yourself fairly easily by using Aurelia's HTTP interceptors (see examples in the docs). Before every request, you can send your token. This can be done with both the conventional aurelia-http-client and the new standard aurelia-fetch-client.
Your code might look like this:
export class MyRestAPI {
static inject () { return [HttpClient]; } // This could easily be fetch-client
constructor (http) {
this.http = http.configure(x => {
x.withBaseUrl(myBaseUrl);
x.useStandardConfiguration();
x.withInterceptor({
request: function (request) {
request.headers.set('XSRF-TOKEN', myAwesomeToken);
return request;
}
});
});
}
...
}
On every request, your token would be sent. You'd have to handle the validation on the server side. You could easily set up your code so that your initial request could grab a token, or you could pass a token back as part of your authentication payload, or if you wanted to you could even store a token in the browser's localstorage and use it that way.
You could even go a step further and implement JWT authentication. If you're using node.js, I have a small blog post that describes how I implemented JWT in Express. There's a plugin on Github called aurelia-auth that handles JWT, and there's a blog post on its implementation on the Aurelia blog as well.
Here is a sample interceptor that reads the token from the response header if it exists and sets it automatically on every request that needs it.
import {Interceptor, HttpResponseMessage, RequestMessage} from "aurelia-http-client";
class CsrfHeaderInterceptor implements Interceptor {
private static readonly TOKEN_HEADER = 'X-CSRF-Token';
private latestCsrfToken: string;
response(response: HttpResponseMessage): HttpResponseMessage {
if (response.headers.has(CsrfHeaderInterceptor.TOKEN_HEADER)) {
this.latestCsrfToken = response.headers.get(CsrfHeaderInterceptor.TOKEN_HEADER);
}
return response;
}
request(request: RequestMessage): RequestMessage {
if (this.latestCsrfToken) {
if (['POST', 'PUT', 'PATCH'].indexOf(request.method) >= 0) {
request.headers.add(CsrfHeaderInterceptor.TOKEN_HEADER, this.latestCsrfToken);
}
}
return request;
}
}
You register it in your http/fetch client with for example:
httpClient.configure((config) => {
config
.withBaseUrl("/api/") // adjust to your needs
.withHeader('Accept', 'application/json') // adjust to your needs
.withHeader('X-Requested-With', 'XMLHttpRequest') // adjust to your needs
.withInterceptor(new CsrfHeaderInterceptor());
});

Categories

Resources