Is it possible to authenticate users using an API via Regular Web App and a server running cron tasks on the same API with a single middleware, so that I can use the same set of routes for both?
Hi @jgarcia
I think we need a few more details here.
Users authenticate via the ULP in your Auth0 tenant using Auth Code or Auth Code + PKCE, resulting in an ID token and an access token for your API.
Cron tasks authenticate via the Client Credentials (M2M) flow, also resulting in an access token for your API.
Both access tokens are signed with the same signing key (the signing key is tenant level).
So you should be able to use the same middleware to verify the signature/issuer.
But I am not sure that is the case you were describing.
John
Thank you for your response @john.gateley. I appreciate any insight you can provide. I am sure what I hope to accomplish is quite easily possible, but my own inexperience is getting in the way.
I have built a simple API with Node and Express. Initially, I was handling authentication for protected API routes by verifying the token via the jsonwebtoken package.
I decided to switch to Auth0 for authentication, because I now have some server tasks which use the same API to run some database maintenance. So, the example you suggested seems exactly what I am hoping to do.
I have followed several quickstarts and read through documentation to try setting this up on my own, but have had trouble getting both user authentication and machine to machine authentication working concurrently.
When I implement user authentication using the express-openid-connect module as in the Express Quickstart it gets configured to use the client id and client secret of my Regular Web App and testing user login and protected routes works as expected.
When I implement machine to machine authentication with the Client Credentials Flow, I can retrieve and pass the token to authenticate to the desired route, using the client id and client secret for my Machine to Machine App.
As you said, these are signed with the same signing key (tenant level), but I haven’t found which middleware method will work to verify both. The authentication middleware for each of the Quickstarts is different.
For my Machine to Machine authentication I am doing the following and calling checkJwt prior to protected routes:
const jwt = require('express-jwt');
const jwksRsa = require('jwks-rsa');
const checkJwt = jwt({
// Dynamically provide a signing key based on the kid in the header and the singing keys provided by the JWKS endpoint.
secret: jwksRsa.expressJwtSecret({
cache: true,
rateLimit: true,
jwksRequestsPerMinute: 5,
jwksUri: `https://${process.env.AUTH0_M2M_DOMAIN}/.well-known/jwks.json`,
}),
// Validate the audience and the issuer.
audience: process.env.AUTH0_M2M_AUDIENCE,
issuer: `https://${process.env.AUTH0_M2M_DOMAIN}/`,
algorithms: ['RS256'],
});
But that isn’t working for users logging in via the browser. For users, I can simply make use of the express-openid-connect package and call requiresAuth() prior to protected routes:
const { auth } = require('express-openid-connect');
const { requiresAuth } = require('express-openid-connect');
const config = {
authRequired: false,
auth0Logout: true,
baseURL: 'http://localhost:3000',
clientID: process.env.AUTH0_WEBAPP_CLIENT_ID,
issuerBaseURL: `https://${process.env.AUTH0_WEBAPP_DOMAIN}/`,
secret: `https://${process.env.AUTH0_WEBAPP_CLIENT_SECRET}/`,
};
// auth router attaches /login, /logout, and /callback routes to the baseURL
app.use(auth(config));
I appreciate any guidance you can offer.
Ah. You need to be using the same Auth0 tenant for your users and your M2M tokens.
You have different issuers/signers now. That won’t work unless you explicitly configure the middleware for that (you can do that, I am not an express expert so I don’t know how, but you probably want them in the same tenant anyway)
John
Thank you for the feedback @john.gateley
I don’t think I have different issuers/signers, as you suggest. I do have only one Auth0 tenant in my account.
I believe I confused things by using different sets of environment variables while testing authentication for the Regular Web App and the Machine to Machine App. They contain some duplicate information (DOMAIN and AUDIENCE) and their relative unique values (CLIENT_ID and CLIENT_SECRET). This simply helped me keep myself clear about which I was testing at the time.
Continuing to think this through, I output the request headers to the console for both authentication paths.
The Machine to Machine authentication is sending the JWT token in the header:
authorization: ‘Bearer [token]’
But accessing the Regular Web App in the browser sets a cookie:
cookie: ‘appSession=[stuff]’
Could this be where I am getting hung up? Is this appSession value a JWK string? I know it isn’t a valid JWT on its own.
The requiresAuth() middleware from express-openid-connect seems to be able to handle this cookie, but doesn’t like it when I pass ‘Bearer [token]’ in the authorization header for Machine to Machine. And vice versa, the checkJwt middleware I can use for Machine to Machine doesn’t know what to do with this cookie.
I hope that is helping to clarify my issue. Thanks again for your time.
It sounds like you are using two different mechanisms here:
Your web app has a session (that’s the cookie) used to verify the user.
Your M2M app is using a M2M cookie, since there is no session or user.
This is kind of a fuzzy case: the “backend” serves as both a backend to your web app AND an API for your M2M app.
You can configure your middleware to respect both the session and the token, or you can make your web app treat the backend as an API instead of a backend, or you can make a true API for the M2M app, and have the backend call that true API (basically, make the backend a wrapper around the current backend, if that makes sense).
I can’t help much with the specifics of configuring your middleware to recognize both, but you should be able to do that.
John
I’m afraid there must be some point I am still missing.
For now, it seems like the simplest thing for me to do is make two sets of routes, one to handle the M2M app which is protected by authentication with the Bearer token in the authorization header and another set for the web app which is protected by authentication with the session cookie from the ULP.
This seems like overkill to me, which was the impetus of my original question, but it does work. I don’t need every single route duplicated for the M2M needs, so it won’t be that bad really. It will work until I can figure out how to do it better.
Thank you for your help, @john.gateley
Hi @jgarcia
That will work, but there should be a way to configure your middleware so that it would accept either method (session cookie or token). I’m not an expert on middleware so I don’t know how, but it should be possible.
John