I have this Javascript code that generates a token to access the Apple Store api. But when I try to use the token I get "401: Unauthorized access" code. I'm not sure if the code I'm using correct or maybe it might be an issue with the id and key I'm using.
console.log("🏃 appStoreConnectAPIFromNode.js running 🏃")
const fs = require('fs');
const jwt = require('jsonwebtoken'); // npm i jsonwebtoken
// You get privateKey, apiKeyId and issuerId from your Apple App Store Connect account
const privateKey = fs.readFileSync("./AuthKey-12345678.p8") // this is the file you can only download once and should treat like a real, very precious key.
const apiKeyId = "12345678"
const issuerId = "issuerId"
let now = Math.round((new Date()).getTime() / 1000); // Notice the /1000
let nowPlus20 = now + 1199 // 1200 === 20 minutes
let payload = {
"iss": issuerId,
"exp": nowPlus20,
"aud": "appstoreconnect-v1"
}
let signOptions = {
"algorithm": "ES256", // you must use this algorithm, not jsonwebtoken's default
header : {
"alg": "ES256",
"kid": apiKeyId,
"typ": "JWT"
}
};
let token = jwt.sign(payload, privateKey, signOptions);
console.log('#token: ', token);
fs.writeFile('Output.txt', token, (err) => {
// In case of a error throw err.
if (err) throw err;
})
// Congrats! the token printed can now be tested with the curl command below
// curl -v https://api.appstoreconnect.apple.com/v1/apps --Header "Authorization: Bearer <YOUR TOKEN>"
// (no '<>' in the curl command)
Related
To recreate code just simply follow this article
Final code is:
'use strict';
// Imports dependencies and set up http server
const
express = require('express'),
bodyParser = require('body-parser'),
app = express().use(bodyParser.json()); // creates express http server
// Sets server port and logs message on success
app.listen(process.env.PORT || 1337, () => console.log('webhook is listening'));
// Creates the endpoint for our webhook
app.post('/webhook', (req, res) => {
let body = req.body;
// Checks this is an event from a page subscription
if (body.object === 'page') {
// Iterates over each entry - there may be multiple if batched
body.entry.forEach(function(entry) {
// Gets the message. entry.messaging is an array, but
// will only ever contain one message, so we get index 0
let webhook_event = entry.messaging[0];
console.log(webhook_event);
});
// Returns a '200 OK' response to all requests
res.status(200).send('EVENT_RECEIVED');
} else {
// Returns a '404 Not Found' if event is not from a page subscription
res.sendStatus(404);
}
});
app.get('/webhook', (req, res) => {
// Your verify token. Should be a random string.
let VERIFY_TOKEN = "<YOUR_VERIFY_TOKEN>"
// Parse the query params
let mode = req.query['hub.mode'];
let token = req.query['hub.verify_token'];
let challenge = req.query['hub.challenge'];
// Checks if a token and mode is in the query string of the request
if (mode && token) {
// Checks the mode and token sent is correct
if (mode === 'subscribe' && token === VERIFY_TOKEN) {
// Responds with the challenge token from the request
console.log('WEBHOOK_VERIFIED');
res.status(200).send(challenge);
} else {
// Responds with '403 Forbidden' if verify tokens do not match
res.sendStatus(403);
}
}
});
Get request route is working fine but when I hit post with this request
curl -H "Content-Type: application/json" -X POST "localhost:1337/webhook" -d '{"object": "page", "entry": [{"messaging": [{"message": "TEST_MESSAGE"}]}]}'
I get this error despite a json is valid
SyntaxError: Unexpected token o in JSON at position 1
It's quite unexpected for me, this code works on server with same node version installed - 14.7.6
So I am writing tests using Mocha, Supertest and Chai. All my API's require an Authorization header which can be gotten from req.headers["authorization"]. This is the current setup of what my test looks like.
...
describe("Upload media files", () => {
it("uploads a file successfully", async () => {
const imagePath = path.join(__dirname, "../../fixtures/profile.jpeg");
const res = await app.post("/api/v1/account/upload") //app is global
.set("Authorization", "ey83hfjksksls...") //right here
.set("content-type", "multipart/form-data")
.attach("media", imagePath);
expect(res.status).to.eql(200);
expect(res.body.data).not.to.be.null;
expect(res.body.data).to.match(/https:/);
});
});
My tests always fail because a 401 is always returned for some reason that I do not know. Here's the function that handle's the decoding of the JWT
async function decodeToken() {
const sessionUser = req.session.userId;
const token = req.headers["authorization"] // token value is present here.
if (!token && !sessionUser) {
return res.status(401).json({
error: "Access denied, no valid credentials.",
});
}
if (token) {
const secret = sails.config.custom.jwtSecret || process.env.JWT_SECRET;
const decoded = jwt.verify(token, secret);
req.me = decoded;
return proceed();
}
const user = await User.findOne({
id: sessionUser,
});
req.me = user;
return proceed();
}
From the function above, when I do a console.log of the token variable, I do get a long string returned which shows that the token is being generated and passed into the .set('Authorization', 'eyeur9339..') header, but the test still fails. I don't know what to do anymore. Any help is appreciated, thank you.
if you use jwt for authorization, use Bearer before token so you should write Bearer, like this :
Bearer 5d5bd7f7e35a86541686f96c9cdd72ed67f5ba223811634e0319af224164823edc2d0090d1f6c2c3b1dec842ea783c71feb443ac2d26e
and for get token from header do like this :
const token = req.headers.authorization.split(' ')[1]
const decodedToken = jwt.verify(token, secret)
I've been following the documentation on how to create an OAuth token for Salesforce Einstein. I have my private key and able to create the JWT token (code on bottom) yet when I try the CURL command as outlined in the documentation (step 4) with the token I've retrieved I get INVALID JWT Assertion error.
Documentation : https://metamind.readme.io/docs/generate-an-oauth-token-using-your-key
This is how I'm creating the signed JWT
require('dotenv').config();
const jwtToken = require('jsonwebtoken');
const payload =
{
"sub": "<EINSTEIN_PLATFORM_SERVICES_USERNAME>",
"aud": "https://api.einstein.ai/v2/oauth2/token",
"exp": <EXPIRATION_SECONDS_IN_UNIX_TIME>
}
const token = jwtToken.sign(payload, process.env.EINSTEIN_KEY, {algorithm : 'RS256'});
Any idea what I'm doing wrong?
It took me a while to figure this out...the problem was the UNIX time I gave was something of the year 1970 -_- so of course, since the JWT token expired I was not able to retrieve an access token from Salesforce Einstein.
I suggest using the following resource to get the correct expiration time in UNIX seconds : https://www.epochconverter.com/.
Here's my final code for generating a JWT Assertion String :
require('dotenv').config()
const fs = require('fs);
const jwt = require('jsonwebtoken');
let privateKey = fs.readFileSync(process.env.PRIVATE_KEY);
const header = {
algorithm: "RS256"
}
const payload = {
"sub": "<EINSTEIN_PLATFORM_SERVICES_USERNAME>",
"aud": "https://api.einstein.ai/v2/oauth2/token",
"exp": 1907891585 //Epoch timestamp of 6/17/2030
}
let token = jwt.sign(payload, privateKey, header);
With this code, I was able to retrieve an access token!
I have created a CLI with which I consume a data from a rest api I build with node.js and express.js. In the cli the user has to login and after that when he performs a query his data are passed as a header using jwt. This I have done with the following code:
The Login.ts file has this part of the code for the header creation:
await connection.query(`SELECT user,pass,email,quota,apikey,privileges FROM users WHERE user=?`,[`${flags.user}`], function (err, result, fields) {
if (err) throw err;
//console.log(`${flags.user}`);
//console.log(result[0].pass);
var u=result[0].user;
var p=result[0].pass;
var e=result[0].email;
var q=result[0].quota;
var a=result[0].apikey;
var p=result[0].privileges;
const password=result[0].pass;
if(bcrypt.compareSync(`${flags.passw}`, password)) {
var fs=require('fs');
var privateKey = fs.readFileSync('private.key');
var jwt=require('jsonwebtoken');
var token = jwt.sign({user:`${flags.user}`,passw:`${flags.passw}`,email: e, quota: q,apikey: a,privileges: p }, privateKey, { algorithm: 'RS256' });
fs.writeFileSync("temptoken.txt",token);
} else {
console.log("Wrong username or password\n");
}
});
After the login the user can perform various queries, where in their code they perform an
axios.get where I pass the header like this:
const axios = require('axios');
const {flags} = this.parse(ActualTotalLoadCommand);
var fs=require('fs');
var token = fs.readFileSync('temptoken.txt');
axios.defaults.headers.common['Authorization']=token;
From my server side file where I have the file entry.routes.js how can I capture the token and then decode it to test for example if the user who performed the query is a valid user?
The entry.routes.js looks like this:
module.exports = app => {
const entry = require("../controlers/entry.controller.js");
// Retrieve a single Entry with Id
var fs=require('fs');
var privateKey = fs.readFileSync('/home/vangelis/softeng/energy/private.key');
var jwt=require('express-jwt');
app.use(
jwt({
secret: privateKey,
credentialsRequired: false,
getToken: function fromHeaderOrQuerystring (req) {
if (req.headers.authorization && req.headers.authorization.split(' ')[0] === 'Bearer') {
return req.headers.authorization.split(' ')[1];
} else if (req.query && req.query.token) {
return req.query.token;
}
return null;
}
}));
app.get("/entry/:Id", entry.findOne);
app.get("/energy/api/ActualTotalLoad/:AreaName/:Resolution/date/:Year-:Month-:Day", entry.findTwo);
As you can see I tried using express-jwt, but I can't make it work.
In your code above you create the header with:
axios.defaults.headers.common['Authorization']=token;
which will result in
Authorization: <the token>
but in the code to read the header :
if (req.headers.authorization && req.headers.authorization.split(' ')[0] === 'Bearer')
you expect a header like this:
Authorization: Bearer <the token>
which is the standard way. So you should add Bearer to the line to create the header.
Beside that, I see you're putting the (encrypted) password into the token. That's something you should avoid. There'sno good reason to do it. You have authenticated the user before you created the token and now, when you receive the token, you verify the authenticity of that token by it's signature.
I am trying to set up Okta authentication on my React App.
On the client side I am able to authenticate successfully and I get the access token. However when I try to authenticate a backend service using OktaJwtVerfier, I get the error message:
'Jwt cannot be parsed. SyntaxError: Unexpected token in JSON at position 0'
I have develop a very simple test program to test the verification of the token, so basically I get authenticated on the browser, I copy paste the jwt token in my small script to test authentication, and it fails with the message above, what am I doing wrong?
const OktaJwtVerifier = require('#okta/jwt-verifier');
const oktaJwtVerifier = new OktaJwtVerifier({
issuer: "https://dev-XXXXX.oktapreview.com/oauth2/default",
clientId: "XXXXXX",
assertClaims: {
'aud': 'api://default',
'cid': "XXXXXX",
},
});
const accessToken = 'Bearer eyJraWQiO.....';
oktaJwtVerifier.verifyAccessToken(accessToken).then((jwt) => {
console.log('auth succesfulll', jwt);
}).catch((e)=> {
console.log(e);
})
The comment by #jps is correct. Your header has a value of Bearer XXXX, where XXXX is the actual JWT string to parse.
Here's an example from the Okta project of how they do it in an Express app:
const authHeader = req.headers.authorization || '';
const match = authHeader.match(/Bearer (.+)/);
if (!match) {
res.status(401);
return next('Unauthorized');
}
const accessToken = match[1];
You can see the code in its full context here.
your code could be modified as follows:
const headerValue = 'Bearer eyJraWQiO.....';
const match = headerValue.match(/Bearer (.+)/);
if (!match) {
throw new Error('your error message here')
}
const accessToken = match[1];
oktaJwtVerifier.verifyAccessToken(accessToken).then((jwt) => {
console.log('auth succesfulll', jwt);
}).catch((e)=> {
console.log(e);
})