I have a middleware to protect a route. And I am checking whether the token is present in headers or not??
if(req.headers.authorization && req.headers.authorization.startsWith('Bearer')){
token = req.headers.authorization.split(" ")[1]
}
*My Question is is it necessary to add something like already did in above code by adding Bearer? is it something that enhance token presence checking or it is just a way.
i think this way should also work
if(req.headers.authorization){
token = req.headers.authorization
}
What will be the difference between these two?
What will be the difference between these two?
if(req.headers.authorization && req.headers.authorization.startsWith('Bearer')){
token = req.headers.authorization.split(" ")[1]
}
It will check if the authorization header exits and assign the authorization string including bearer with it and you need to segregate it first to verify the token
if(req.headers.authorization){
token = req.headers.authorization
}
It will check the authorization exists or not and if it does, it includes the Bearer. It is fine to check, however, not needed unless you follow same approach for all the APIs
You should do this:
const jwt = require('jsonwebtoken');
const verifyToken = async (req, res, next) => {
const { authorization } = req.headers;
const token = authorization.split(' ')[1];
try {
const jt = await jwt.verify(token, 'key');
//do something
} catch (error) {
res.status(401).send("Unauthorized");
}
}
module.exports = verifyToken;
1. The difference between the 2 code.
The 2 codes have the same objective: get the JWT in the header Authorization. They will behave differently based on the way you send the JWT in the request.
2. Explain
Usually, we send the JWT token to the server by using Bearer Authentication. In this mode :
The client must send this token in the Authorization header when
making requests to protected resources:
Authorization: Bearer [[token]]
(Source : https://swagger.io/docs/specification/authentication/bearer-authentication/)
So, to retrieve the token from the request, we must :
Firstly, ensure that the request has the Authorization header in the right format
Secondly, split the authorization header by space, and the token is the second element of the array.
That is what the first version does.
(Note: It should check whether the header contains a space or not before splitting the string
if(req.headers.authorization && req.headers.authorization.startsWith('Bearer ')){
token = req.headers.authorization.split(" ")[1]
}
)
3. Conclusion
The first version is based on a popular standard and should be use with a small modification :)
Related
I have a nestJS backend protected with Auth0. I am able to successfully access the backend from react-admin by including the access token in the authorization header (Authorization: Bearer ACCESS_TOKEN)
However, I seem to have an issue when trying to access the same backend from a Swift iOS app. I have followed the Auth0 tutorials and am able to confirm successful user login and access to user profile. However, when I try to make a request to the nestJS backend, I receive a 401 Unauthorized error. Interestingly, nothing is recorded in the Auth0 logs.
Link to Auth0 tutorial: https://auth0.com/docs/quickstart/native/ios-swift/04-calling-apis
let path = "\(baseURL)\(endpoint.rawValue)"
guard let url = URL(string: path)
else { preconditionFailure("Bad URL") }
var headers: [String:String] = [:]
headers["Content-Type"] = "application/json"
// if access token is set then set Authorization headers
if (accessToken != nil) {
headers["Authorization"] = "Bearer \(accessToken!)"
print("Bearer \(accessToken!)")
}
var request = URLRequest(url: url)
request.httpMethod = "\(method)"
request.allHTTPHeaderFields = headers
// check if body exists
if (body != nil) {
request.httpBody = body!
}
let dataTask = URLSession.shared.dataTask(with: request) {
(data, response, error) in
guard error == nil
else { completion(.failure(.serverError)); return }
do {
guard let data = data
else { completion(.failure(.serverError)); return }
guard let object : [[String: AnyObject]] = try JSONSerialization.object(with: data) as? [[String: AnyObject]]
else {
print("Unable to convert from data")
return
}
guard let json = try? JSONSerialization.data(withJSONObject: object, options: .prettyPrinted)
else {
print("Unable to prettify")
return
}
guard let jsonString = String(data: json, encoding: .utf8)
else {
print("Unable to convert to string")
return
}
print("JSON: \(jsonString)")
completion(Result.success(object))
} catch {
completion(Result.failure(.parsingError))
}
}
dataTask.resume()
baseURL is a string that points to my nestJS backend.
endpoint is an enum of endpoints, for example \user
Using Proxyman I am able to confirm that the endpoint is hit with the correct headers. Screenshot below.
Additionally, using postman I am able to successfully login and also make a get request to protected data. Screenshot below.
Any ideas to what might be the cause? Let me know if I should add any additional details.
UPDATE
I decoded the successful (react-admin) and unsuccessful (iOS) JWT tokens and noticed the following differences:
aud in the successful JWT contains an array of audiences that include the API registerd on Auth0 as well as an auth0 endpoint https://xxxxx.us.auth0.com/userinfo
azp is only present in the successful JWT and contains my clientID
aud in the unsuccessful token contains the clientID
scope and permissions is missing from unsuccessful token.
Ps. also posted on Auth0 Community
https://community.auth0.com/t/access-token-when-obtained-from-ios-results-in-401-unauthorized-while-from-react-admin-is-ok/71115
The problem was that audience was set when requesting access tokens from the react-admin, while I did not include this in the swift login implementation.
Decoding the JWT on jwt.io and the following thread lead to this conclusion.
https://community.auth0.com/t/access-token-too-short-jwt-malformed/9169/11?u=kennethphough
Adding the audience in the following code resulted in the correct jwt being returned and successful access to backend.
Auth0
.authentication()
.login(
usernameOrEmail: self.email,
password: self.password,
realm: "Username-Password-Authentication",
audience: "<YOUR_AUDIENCE>", // <- This is what I forgot
scope: "openid profile email"
)
In example project provided by Microsoft here which uses Authorization code flow the acquireTokenByCode method does not return refresh tokens.
From #azure/msal-node here refresh token is not mentioned.
Result returned from the authority's token endpoint.
uniqueId - oid or sub claim from ID token
tenantId - tid claim from ID token
scopes - Scopes that are validated for the respective token
account - An account object representation of the currently signed-in user
idToken - Id token received as part of the response
idTokenClaims - MSAL-relevant ID token claims
accessToken - Access token received as part of the response
fromCache - Boolean denoting whether token came from cache
expiresOn - Javascript Date object representing relative expiration of access token
extExpiresOn - Javascript Date object representing extended relative expiration of access token in case of server outage
state - Value passed in by user in request
familyId - Family ID identifier, usually only used for refresh tokens
please ensure your MSAL authorization code request includes the offline_access scope.
You could use MSAL.js to get token in this case, there is acquireTokenSilent method, it can perform silent renewal of tokens, which means you are no need to get the refresh token by yourself.
Popup
var request = {
scopes: ["Mail.Read"]
};
msalInstance.acquireTokenSilent(request).then(tokenResponse => {
// Do something with the tokenResponse
}).catch(async (error) => {
if (error instanceof InteractionRequiredAuthError) {
// fallback to interaction when silent call fails
return myMSALObj.acquireTokenPopup(request);
}
}).catch(error => {
handleError(error);
});
Redirect
var request = {
scopes: ["Mail.Read"]
};
msalInstance.acquireTokenSilent(request).then(tokenResponse => {
// Do something with the tokenResponse
}).catch(error => {
if (error instanceof InteractionRequiredAuthError) {
// fallback to interaction when silent call fails
return myMSALObj.acquireTokenRedirect(request)
}
});
It's designed to not return the refresh token if you are using #azure/msal-node.
As they stated in the discussion, the refresh token is handled background, inside the library itself for better security, which I also disagree with.
However, if you insist to have the token, you can manually call the API to the AzureAD endpoint.
I have a bot in NodeJS connected to Google Chat using HTTPs endpoints. I am using express to receive requests. I need to verify that all requests come from Google, and want to do this using the Bearer Token that Google Sends with requests.
My problem is that I am struggling to find a way to verify the tokens.
I have captured the token and tried a GET reuqes to https://oauth2.googleapis.com/tokeninfo?id_token=ey... (where ey... is the token start).
Which returns:
"error": "invalid_token",
"error_description": "Invalid Value"
}
I have tried what Google recommends:
var token = req.headers.authorization.split(/[ ]+/);
client.verifyIdToken({
idToken: token[1],
audience: JSON.parse(process.env.valid_client_ids)
}).then((ticket) => {
gchatHandler.handleGChat(req.body, res);
}).catch(console.error);
And get the following error:
Error: No pem found for envelope: {"alg":"RS256","kid":"d...1","typ":"JWT"}
Any idea where I should head from here?
Edit: https://www.googleapis.com/service_accounts/v1/metadata/x509/chat#system.gserviceaccount.com found this, investigating how to use it. The kid matches the one I get.
Worked it out, eventually.
You need to hit: https://www.googleapis.com/service_accounts/v1/metadata/x509/chat#system.gserviceaccount.com to get a JSON file containing the keys linked to their KIDs.
Then when a request arrives, use jsonwebtoken (NPM) to decode the token and extract the KID from the header.
Use the KID to find the matching public key in the response from the website above, then use the verify function to make sure the token matches the public key.
You also need to pass the audience and issuer options to verify, to validate that it is your particular service account hitting the bot.
The solution above maybe the correct for Google Chat, but in my experience Google services (e.g. Google Tasks) use OIDC tokens, which can be validated with verifyIdToken function.
Adding my solution here, since your question/answer was the closest thing I was able to find to my problem
So, In case if you need to sign a request from your own code
on client, send requests with OIDC token
import {URL} from 'url';
import {GoogleAuth} from 'google-auth-library';
// will use default auth or GOOGLE_APPLICATION_CREDENTIALS path to SA file
// you must validate email of this identity on the server!
const auth = new GoogleAuth({});
export const request = async ({url, ...options}) => {
const targetAudience = new URL(url as string).origin;
const client = await auth.getIdTokenClient(targetAudience);
return await client.request({...options, url});
};
await request({ url: 'https://my-domain.com/endpoint1', method: 'POST', data: {} })
on the server, validate OIDC (Id token)
const auth = new OAuth2Client();
const audience = 'https://my-domain.com';
// to validate
const token = req.headers.authorization.split(/[ ]+/)[1];
const ticket = await auth.verifyIdToken({idToken: token, audience });
if (ticket.getPayload().email !== SA_EMAIL) {
throw new Error('request was signed with different SA');
}
// all good
Read more about Google OpenID Connect Tokens
I am trying to understand how token based authentication strategy uses the token (e.g.: JWT) to access the data specific to a user. All I see explained in search results is the part when the user supplies the username and password and then the token is created and verified with each subsequent call.
Le't say I have a Node.JS service with two MongoDB collections: Users and UserMessages. Each entry from UserMessages contains a UserID and a Message.
If an authenticated user wants to see all the messages pertaining to that user, how do I (programatically) know how to filter the correct messages? How do I make the correlation between the token and the user identity (e.g.: UserID or anything else that helps identify the user) that I need for querying the message collection in the next step?
I just wish to understand the concept. I couldn't find anywhere a clear example where the user identity is obtained in code. Example:
apiRoutes.use(function(req, res, next) {
var token = req.body.token || req.param('token')
|| req.headers['x-access-token'];
if (token) {
jwt.verify(token, app.get('superSecret'), function(err, decoded) {
if (err) {
return res.json({ success: false, message:
'Failed to authenticate token.' });
} else {
req.decoded = decoded;
next();
}
});
}
});
The JWT contains a payload.
Typically, you put the user's ID in this payload. You can put anything you like in there, but since the token is sent on every request you want to keep the payload small. Also, because the JWT is base-64 encoded, anyone can decode the token and see the contents of the payload.
However, because only your server knows the "secret" for the JWT, no one can alter the payload or otherwise create a phony JWT... your server will consider such tokens invalid.
The above code seems to be decoding the payload and storing it on the req object. When it decodes the JWT it is also verifying the signature, so if the decode is successful you can trust the data in the payload. Now you can use the user ID from the payload to get the data you desire.
After following the guide for creating a custom identity provider for azure mobile services I can easily generate the appropriate tokens. The code is pretty simple and looks like this:
var userAuth = {
user: { userId : userId },
token: zumoJwt(expiry, aud, userId, masterKey)
}
response.send(200, userAuth);
The definitions for the parameters and code for zumoJwt are located at the link. Azure automatically decodes the token and populates the user on the request object which is what I'd like to simulate.
Basically I'd like to to decrypt the token on the serverside via Node (not .net).
What I ended up doing to validate the token is the following (boiled down). This seems to be about what the azure mobile services is doing on routes that require authorization.
var jws = require('jsw'); // https://github.com/brianloveswords/node-jws
function userAuth() {
var token = ''; // get token as header or whatever
var key = crypto.createHash('sha256').update(global.masterKey + "JWTSig").digest('binary');
if (!jws.verify(token,key)) {
// invalid token logic
} else {
var decode = jws.decode(token)
req.user = {
userId: decode.payload.uid.split(';')[0].split('::')[0]
};
next();
}
}
app.use(authChecker);
The tokens aren't really encrypted - they're just signed. The tokens have the JWT format (line breaks added for clarity):
<header>, base64-encoded
"."
<envelope>, base64-encoded
"."
<signature>, base64-encoded
If you want to decode (not decrypt) the token in node, you can split the value in the . character, take the first two members, base64-decode them (var buffer = new Buffer(part, 'base64')), and convert the buffer to string (buffer.toString('utf-8')).
If you want to validate the token, just follow the same steps you need to re-sign the first two parts of the token (header + '.' + envelope) with the master key, in the same way that the token was created, and compare it with the signature you received on the original token.