I'am running a nodejs/express application as a backend solution for my current project. The application is using passport-jwt to secure some routes with JWT as header Authorization for a route, let's call this route secure-route. Now I'm running a second application which needs to access secure-route without the necessary Authorization header. The necessary Authorization header is generated by a login route after the user has authorized successfully.
The problem is, that I don't want to provide a (fake) jwt Authorization header (which shouldn't expire). The second application/server should access my first application with a more appropriate authorization strategy like basic-auth.
I thought about making secure-route private in another router module so I can access this private route by maybe rerouting.
So how can I make an express route private accessible ? Or is there a solution for authenticating a backend/server without affecting the current authentication strategy ?
EDIT :
both backends running on a serverless structure on AWS
Assuming this second application you mention is running either on the same server or on another server in the same network, then you can do the following:
Create a new web server on a non-standard port that is not accessible from the general internet (just a few lines of code with Express).
Run that new web server in the same nodejs process that your existing server with the secure-route is running on.
In that new server, create a route for the private access. In that private route, do not implement any access control.
Put the code for the route into a separately callable function.
When that new server route gets hit, call the same function that you use to implement the secure route in the other server.
Verify that there is no access to your second server's port from the internet (firewall settings).
You could also just take your one existing server and route and allow access without the authorization header only when accessed from a specific IP address where your other app is running.
If you can't use anything about the network topology of the server to securely identify your 2nd app when it makes a request, then you have to create a secret credential for it and use that credential (akin to an admin password or admin certificate). Or, switch to an architecture where you can use the network topology to identify the 2nd app.
You should make a middleware and use it like this
/Starting Point of the Project/
let CONGIG = require('./config');
let middleware = require('./middleware');
let app = express();
app.use(middleware.testFunction);
require('./route')(app);
'use strict';
let middleware = {
testFunction : function(req,res,next){
var condition = ''; /* now here you can write your logic on condition that when should be the condition be true and when it shoudld not be true based on the req.url , if the user is trying to access any public url you can simply allow the true part of the condition to run and if the person is accessing a private part of route then you can check for additional parameters in header and then set the condition true and if not you must send an error msg or a simple message as you are not allowed to access the private parts of the web application. */
if(condtion){
next();
} else {
res.send('error');
}
}
}
So by designing a middlware you can basically seperate the logic of private and public routes and on what condition a route is public or private in a seperate module that will deal with it , it is little bit difficult to understand but it is better to first filter out public and private route than latter checking . In this way on the very initial hit we can differentiate the private and public routes.
Related
Intro
My application is composed of 3 services:
Gateway: Handles all of the requests. Passes them to the appropriate service.
Authentication: Hands out JWT tokens stored in cookies for user login.
Shortener: Simple service that allows you to generate and retrieve shortened URLs.
Requests to '/auth' should be forwarded directly from the gateway to authService. The remaining requests are forwarded to the shortenService. Everything works fine as is. Here is some sample code:
const app = express();
const { createProxyMiddleware } = require('http-proxy-middleware');
const PORT = process.env.PORT || 4000;
const authService = createProxyMiddleware({ target: 'http://localhost:3001/'});
const shortenService = createProxyMiddleware({ target: 'http://localhost:3000/'});
app.use('/auth', authService);
app.use('/', shortenService);
app.listen(PORT, () => {
console.log(`Server listening on port ${PORT}`)
});
Problem
My goal is to have all requests to the shortService first run through a function on my authService that verifies the JWT token. Also, I would like to set some values on my req object (ex: req.userId). Some solutions come to mind:
Make the JWT key accessible to my Gateway and have the Gateway run the
JWT verify command.
On the Gateway, extract the JWT token from the cookie. Write an API on authService that accepts token as input and returns the decoded token as JSON. Have the Gateway use this API and then set the req object values on the Gateway using the returned JSON.
Proxy all requests to authService and then let authService proxy requests to the shortenService.
Move the authentication service to my Gateway.
I can think of issues for all of these. #1 means my JWT key is now on 2 different services. #2 seems weird. #3 defeats the purpose of having a Gateway. I'd rather avoid #4.
Is there an option where I could actually pass the req to the authService, allow the authService to run the decode AND to set the values on the req object, return to the Gateway, and then move on to the shortenService? Is this necessarily more desirable than #2?
For example, it would be great if this could work but the requests seem to terminate at my authService when I tried it out:
app.use('/', authService, shortenService);
Option #2 seems ideal, not sure why you'd call it weird. You could indeed have your Gateway use the authService as an API:
Gateway basically checks for the cookie (if there is none, no need to even contact authService), passes it on to authService, then adds the response in e.g. req.auth.
The http-proxy-middleware middleware allows you to modify the request first, e.g. add another header with the JSON representation of req.auth. On your other services (i.e. shortenService) you can add a quick middleware that will decode the header (if present) and assign it to req.auth.
This approach give all your (future) services the exact same req.auth data, while only the Gateway had to communicate with the authService. It also allows some other handy things, e.g. only allowing authenticated services to even send requests to some of your services.
I'm a beginner in gRPC and as my first challenge, I'm building a Node JS platform composed of some gRPC microservices (according to API-Gateway pattern). I would like to restrict all access from external sources - only the gateway itself will be able to reach my internal struct.
After some time searching, I found out 3 ways to limit the access:
1 - HTTP authentication;
2 - Token authentication;
3 - TSL/SSL authentication;
My Gateway already has an auth mechanism - JWT middleware. I don't want to copy it for each microservice and generate a lot of code redundancy.
I would like to get some way to filter my requests by IP in each internal microservice and allow or disallow its access. In a nutshell, I want to ensure that only the Gateway IP can access all internal microservices.
Here, the component diagram showing my initial architecture:
We can do it easily in an Express API:
// Express example
app.use(function (req, res, next) {
if (req.ip !== '1.2.3.4') { // Wrong IP address
res.status(401);
return res.send('Permission denied');
}
next(); // correct IP address, continue middleware chain
});
Is there some way to build something like that using gRPC?
Thank you very much.
From what i understand the purpose of the Authorization Code flow is to exchange the auth code for access token. This exchange happens between the server which serves the page and authorization server so that the actual access token is not exposed to the client user.
How should the page server store the access token once it is obtained? I was learning from a Pluralsight example in which there is this part of code:
public static HttpClient GetClient()
{
HttpClient client = new HttpClient();
var accessToken = RequestAccessTokenAuthorizationCode();
client.SetBearerToken(accessToken);
client.BaseAddress = new Uri(IdentityConstants.API);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
return client;
}
private static string RequestAccessTokenAuthorizationCode()
{
// did we store the token before?
var cookie = HttpContext.Current.Request.Cookies.Get("ClientMVCCookie.AuthCode");
if (cookie != null && cookie["access_token"] != null && !string.IsNullOrEmpty(cookie["access_token"]))
{
return cookie["access_token"];
}
// no token found - request one
// we'll pass through the URI we want to return to as state
var state = HttpContext.Current.Request.Url.OriginalString;
var authorizeRequest = new IdentityModel.Client.AuthorizeRequest(
IdentityConstants.AuthEndoint);
var url = authorizeRequest.CreateAuthorizeUrl(IdentityConstants.MVCClientSecret, "code", "management secret",
IdentityConstants.MVCAuthCodeCallback, state);
HttpContext.Current.Response.Redirect(url);
return null;
}
}
This will cause each request to check if there is an access token stored in the cookie. If not then the flow will be initiated. The callback looks like this:
public class CallbackController : Controller
{
// GET: STSCallback
public async Task<ActionResult> Index()
{
// get the authorization code from the query string
var authCode = Request.QueryString["code"];
// with the auth code, we can request an access token.
var client = new TokenClient(
IdentityConstants.TokenEndoint,
"mvc_client_auth_code",
IdentityConstants.MVCClientSecretAuthCode);
var tokenResponse = await client.RequestAuthorizationCodeAsync(
authCode,
IdentityConstants.MVCAuthCodeCallback);
// we save the token in a cookie for use later on
var cookie = Response.Cookies["ClientMVCCookie.AuthCode"];
cookie.Expires = DateTime.Now.AddMinutes(1);
cookie["access_token"] = tokenResponse.AccessToken;
// get the state (uri to return to)
var state = Request.QueryString["state"];
// redirect to the URI saved in state
return Redirect(state);
}
}
Doesn't storing the access token in the cookie defeath the whole purpose of the authorization code flow? The cookie will be transmitted to the client browser thus exposing it to the client? Am i missing something? It this is not the correct way to store the token, how should it be stored?
The client, in OAuth terminology, is the component that makes requests to the resource server, in your case, the client is the server of a web application (NOT the browser).
Therefore, the access token should be stored on the web application server only. It should not be exposed to the browser, and it doesn't need to, because the browser never makes any direct requests to the resource server. It talks to the web application server instead, which in turn makes requests to the resource server using the access token.
How the browser authenticates itself with the web application server has nothing to do with OAuth 2.0. For example, it might be a regular session cookie, and the web application server might associate each session or each user with an access token.
The token request, which exchanges the authentication code for an access token, is done by the web application server, and the web application server should authenticate itself with the authorization server (e.g., using a shared client_secret).
Authorization code flow ensures that the client can be authenticated, which protects against malicious clients posing as legitimate clients. Not all web application clients have a server component, and in some cases, requests to the resource server are made directly by JavaScript code in the browser. In such situations, the browser is the client, and the access token must be stored by the browser (in a JavaScript variable, local storage or a cookie). In this case, the client cannot be authenticated (but a reasonable amount of security may be achieved by using TLS and the server redirecting only to registered endpoint URLs).
Recommended reading regarding OAuth 2.0 security: https://www.rfc-editor.org/rfc/rfc6819#section-4.3.3 (RFC 6819)
The cookie is never exposed to the browser. It is part of the response returned from the authorization server to the client, which itself is a server, not a browser. The CallbackController, which implements the redirection endpoint, extracts the cookie from the response.
The cookie is never passed on to the browser. How the browser authenticates itself with the client's application server is not shown in your sample code, and it is not part of OAuth.
The authorization server could store the token in the request body (e.g., in JSON format) rather than in a cookie. However, this makes no difference, because the client can see and process the entire response anyway.
For details, see my other answer: https://stackoverflow.com/a/44655679/2279059
Side note: The CallbackController uses state to store a final URL to redirect the browser to. This is non-standard but works. However, state is actually meant to protect the redirection endpoints against CSRF attacks. The CallbackController does not validate state but blindly redirects to whatever URL was given. Probably this detail was left out, because the code was meant as an example. However, it shows that this code is probably not entirely production-ready.
If you are going to request a rest resource from the browser, the flow you need is Implicit Grant. Check this Auth0 post to decide between the flows https://auth0.com/docs/api-auth/which-oauth-flow-to-use. If you want to use the access token from server you should store the Authorization code and generate an access token every time you need it, access token is not intended to live more than 5 minutes, you don't need to store it
I'm trying add Auth0 authentication to my single-page-app. My app is running under a domain, say app.mycompany.com, whereas the api used by this app is running under a different domain, say api.mycompany.com.
I'm aware of this thread:
Single Sign On (SSO) solution/architecture for Single Page App (SPA)
and the auth0 articles and github repositories linked by here. But I have a feeling that my scenario is slightly simpler, as I don't necessarily want to have single-sign-on between several different single-page-apps. For a start I just want the seperation between the API and the app.
Here is what I have tried already:
I already started from the article React Login With Auth0 and downloaded the starter project. I can surely login without problems and it will leave me with an id_token in my localStorage containing a JWS issued by Auth0.
I can also login directly on api.mycompany.com (my FeathersJS API application) and I can see that during the OAuth redirecting process, the id_token token is magically translated to a feathers-jwt token issued by my Feathers application containing the internal ID of the user-object matching the auth0-ID. I also have implemented the logic used to map from the Auth0-ID to my internal ID. Furthermore all my Feathers hooks such as validation of token and population of the user are working.
What I cannot figure out is how to alter the react-application running under app.mycompany.com with an Auth0-token in localStorage, so that this token is translated to a feathers-jwt token by api.mycompany.com, in such a way that all succeeding API-calls automatically has the feathers-jwt token included so the API can validate the user and return the right data.
Any suggestions on how to proceed will be greatly appreciated.
A couple of more background details:
The api is built on node.js and featherjs (which basically is an extension of Express)
The single-page-app is built on ReactJS and is served by a simple Express server, but it could be served by any server that can serve static files over http. The single-page-app makes http-requests to the api to read data and perform operations.
The api has the following lines of code taking care of the authentication:
const authentication = require('feathers-authentication');
const Auth0Strategy = require('passport-auth0').Strategy;
app.configure(authentication({
local:false,
token: {
secret: 'mysecret',
payload: ['email', 'auth0Nickname'],
issuer: 'mycompany'
},
idField: 'id',
shouldSetupSuccessRoute: false,
auth0: {
strategy: Auth0Strategy,
domain: 'mycompany.eu.auth0.com',
'clientID': 'xxx',
'clientSecret': 'yyy'
}
}));
I had exactly the same problem as you, I wanted to authenticate a user from a single page application, calling the API located on an other server.
The official auth0 example is a classic Express web application that does authentication and renders html page, but it's not a SPA connected to an API hosted on an other domain.
Let's break up what happens when the user authenticates in this example:
The user makes a request calling /auth/auth0 route
The user is automatically redirected to the Auth0 authentication process (Auth0 login form to choose the provider and then the provider login screen)
The user is redirected to /auth/success route
/auth/success route redirects to the static html page public/success.html, also sending a jwt-token cookie that contains the user's token
Client-side, when public/success.html loads, Feathers client authenticate() method reads the token from the cookie and saves it in the local storage.
From now, the Feathers client will authenticate the user reading the cookie from the local storage.
I tried to adapt this scenario to a single-page application architecture, implementing the following process:
From the SPA, call the authentication API with a source query string parameter that contains the SPA URL. For example: http://my-api.com/auth/auth0?source=http://my-spa.com
Server-side, in /auth/auth0 route handler, create a cookie to store that URL
After a successful login, read the source cookie to redirect the user back to the SPA, sending the JWT token in a cookie.
But the last step didn't work because you can't set a cookie on a given domain (the API server domain) and redirect the user to an other domain! (more on this here on Stackoverflow)
So actually I solved the problem by:
server-side: sending the token back to the client using the URL hash.
client-side: create a new html page that reads the token from the URL hash
Server-side code:
// Add a middleware to write in a cookie where the user comes from
// This cookie will be used later to redirect the user to the SPA
app.get('/auth/auth0', (req, res, next) => {
const { origin } = req.query
if (origin) {
res.cookie(WEB_CLIENT_COOKIE, origin)
} else {
res.clearCookie(WEB_CLIENT_COOKIE)
}
next()
})
// Route called after a successful login
// Redirect the user to the single-page application "forwarding" the auth token
app.get('/auth/success', (req, res) => {
const origin = req.cookies[WEB_CLIENT_COOKIE]
if (origin) {
// if there is a cookie that contains the URL source, redirect the user to this URL
// and send the user's token in the URL hash
const token = req.cookies['feathers-jwt']
const redirectUrl = `${origin}/auth0.html#${token}`
res.redirect(redirectUrl)
} else {
// otherwise send the static page on the same domain.
res.sendFile(path.resolve(process.cwd(), 'public', 'success.html'))
}
})
Client-side, auth0.html page in the SPA
In the SPA, I created a new html page I called auth0.html that does 3 things:
it reads the token from the hash
it saves it in the local storage (to mimic what the Feathers client does)
it redirects the user to the SPA main page index.html
html code:
<html>
<body>
<script>
function init() {
const token = getToken()
if (!token) {
console.error('No auth token found in the URL hash!')
}
// Save the token in the local storage
window.localStorage.setItem('feathers-jwt', token)
// Redirect to the single-page application
window.location.href = '/'
}
// Read the token from the URL hash
function getToken() {
const hash = self.location.hash
const array = /#(.*)/.exec(hash)
if (!array) return
return array[1]
}
init()
</script>
</body>
</html>
And now in the SPA I can use the Feathers client, reading the token from the local storage when the app starts.
Let me know if it makes sense, thank you!
If you haven't done so, you should follow this article (React Login with Auth0) to implement the authentication on your React application. If you already tried to follow it, update your question with specific issues you faced.
Even though you currently not need SSO, the actual implementation of the authentication in your application will not vary much. By using Auth0 enabling SSO across your apps is mostly enabling configuration switches.
Finally for a full reference with all the theory behind the security related aspects of your exact scenario check:
Auth0 Architecture Scenarios: SPA + API
Update:
The full scenario I linked too covers the most comprehensive scenarios where an API is accessed by a multitude of client applications that may even be developed by third-parties that do not own the protected API, but want to access the data behind it.
It does this by leveraging recent features that are currently only available in the US region and that at a very high level can be described as an OAuth 2.0 authorization server delivered as a service.
Your particular scenario is simpler, both the API and client application are under control of the same entity, so you have another option.
Option 1 - Leverage the API authorization through Auth0 US region only (for now)
In this situation your client application, at authentication time, would receive an id_token that would be used to know the currently authenticated user and would also receive an access_token that could be used to call the API on behalf of the authenticated user.
This makes a clear separation between the client application and the API; the id_token is for client application usage and the access_token for API usage.
It has the benefit that authorization is clearly separated from authentication and you can have a very fine-grained control over authorization decisions by controlling the scopes included in the access token.
Option 2 - Authenticate in client application and API in the same way
You can deploy your client application and API separately, but still treat them from a conceptual perspective as the same application (you would have one client configured in Auth0 representing both client-side and API).
This has the benefit that you could use the id_token that is obtained after authentication completes to know who the user was on the client-side and also as the mechanism to authenticate each API request.
You would have to configure feathers API to validate the Auth0 id_token as an accepted token for accessing the API. This means that you don't use any feathers based on authentication on the API, that is, you just accept tokens issued by Auth0 to your application as the way to validate the access.
I'm wondering how to create an endpoint (GET, POST, etc...) that can be accessed without any authentication. this is my code:
router.use(AuthenticationManager.ensureAuthenticated());
router.use('/', require('./index'));
router.use('/path-1', require('./path1'));
router.use('/path-2', require('./path2'));
All the endpoints will enjoy the Authentication Manager. How disable that authentication manager only in some endpoints inside ./path1 or ./path2 ?
The regular method of doing this is defining those endpoints before the AuthenticationManager middleware:
router.use('/path-1/unprotected', require('./path1'));
router.use('/path-2/unprotected', require('./path2'));
router.use(AuthenticationManager.ensureAuthenticated());
It depends on what exactly path1.js and path2.js are exporting if it works as-is, or if you need to do a bit of rewriting.
you can use authentication for specific endpoint by using this:
app.post('/profile', AuthenticationManager.ensureAuthenticated , userController.getUserProfile);