Building a basic application where users can find Service Providers using MEAN Stack, and after negotiations are over, agreements are auto generated and have to be signed by both parties.
Got Stuck on generation of JWT Token for authentication.
Steps I followed are:
Generate a url for obtaining consent from user and pass it to frontend. Users will be redirected and permissions can be granted from there.
var url = "https://account-d.docusign.com/oauth/auth?response_type=code&scope=signature&client_id=42017946-xxxx-xxxx-xxxx-81b0ca97dc9a&redirect_uri=http://localhost:4200/authorization_code/callback";
res.status(200).json({
status: 1,
message: 'Fetched',
value: url
});
After successful redirection with code in URL, API call is made to backend for the generation of JWT token.
Token is generated as follows:
var jwt = require('jsonwebtoken');
var privateKey = fs.readFileSync(require('path').resolve(__dirname, '../../src/environments/docusign'));
const header = {
"alg": "RS256",
"typ": "JWT"
};
const payload = {
iss: '42017946-xxxx-xxxx-a5cd-xxxxxx',
sub: '123456',
iat: Math.floor(+new Date() / 1000),
aud: "account-d.docusign.com",
scope: "signature"
};
var token = jwt.sign(payload, privateKey, { algorithm: 'RS256', header: header });
Private key used above is from docusign admin panel.
iss -> Integration key against my app.
sub -> user id in the drop down of user symbol in admin panel
Obtain the access token
const axios = require('axios');
axios.post('https://account-d.docusign.com/oauth/token',
{
grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
assertion: token
})
.then(resposne => {
console.log(response);
})
.catch(err => {
if (err.response) {
console.log(err);
} else if (err.request) {}
else {}
})
But I am constantly getting error: { error: 'invalid_grant', error_description: 'no_valid_keys_or_signatures' }
I would suggest using the node.JS SDK or npm package and using the build-it JWT method to authenticate. The code would look like this:
(click here for GitHub example)
DsJwtAuth.prototype.getToken = async function _getToken() {
// Data used
// dsConfig.dsClientId
// dsConfig.impersonatedUserGuid
// dsConfig.privateKey
// dsConfig.dsOauthServer
const jwtLifeSec = 10 * 60, // requested lifetime for the JWT is 10 min
scopes = "signature", // impersonation scope is implied due to use of JWT grant
dsApi = new docusign.ApiClient();
dsApi.setOAuthBasePath(dsConfig.dsOauthServer.replace('https://', '')); // it should be domain only.
const results = await dsApi.requestJWTUserToken(dsConfig.dsClientId,
dsConfig.impersonatedUserGuid, scopes, rsaKey,
jwtLifeSec);
const expiresAt = moment().add(results.body.expires_in, 's').subtract(tokenReplaceMin, 'm');
this.accessToken = results.body.access_token;
this._tokenExpiration = expiresAt;
return {
accessToken: results.body.access_token,
tokenExpirationTimestamp: expiresAt
};
Related
Hi I have a code from https://github.com/Azure-Samples/ms-identity-javascript-react-spa
I changed it a little bit, so instead calling an Microsoft Graph API endpoint, I call mine endpoint on localhost:7000.
So it basically starts with me logging in (here i did not change enything). Then there is this function which acquires token:
const { instance, accounts } = useMsal();
const [graphData, setData] = useState(null);
function RequestProfileData() {
// Silently acquires an access token which is then attached to a request for MS Graph data
instance
.acquireTokenSilent({
...loginRequest,
account: accounts[0],
})
.then((response) => {
callMyEndpoint(response.accessToken).then((response) =>
setData(response)
);
});
}
it uses function callMyEndpoint which looks like this:
export async function callMyEndpoint(accessToken) {
const headers = new Headers();
const bearer = `Bearer ${accessToken}`;
headers.append("Authorization", bearer);
const options = {
method: "POST",
headers: headers,
};
return fetch("http://localhost:7000/myendpoint", options)
.then((response) => response.json())
.catch((error) => console.log(error)) // if the user is not logged in- catch an error;
}
Now, onto my Node.js backend application where the http://localhost:7000/myendpoint is served.
app.post("/myendpoint", async (req, res) => {
console.log("TOKEN", req.headers.authorization); // it is being printed here, everything seems fine.
// here i would like to check whether the token is valid
// if req.headers.authorization == AZURE_TOKEN?
// How to do this?
});
And now the question is? How to check in backend if the token send from frontend is valid for the user, so only logged users, or users which are added in my app registration in azure can post onto this request?
You can use the libraries such as validate-azure-ad-token or you can write your own logic using jsonwebtoken
Here I have my custom logic for that first you will need client_id , tenat_id and scope name.
I am assuming you already have client and tenant id and for scope name it will be available in the Expose Api tab of your app registration.
Here I have console app which will take your token and try to validate it.
var jwt = require('jsonwebtoken');
var token = 'your Token';
var clientid = '' ;
var tenantid = "" ;
var scope = "";
// Create an audiance variable
var audiance = 'api://'+clientid;
// decoded token
var decodedToken = jwt.decode(token , {complete :true});
if((decodedToken.payload.aud==audi)&&(decodedToken.payload.scp==scope)&&(decodedToken.payload.tid==tenantid))
{
console.log("The token is valid");
}
else
{
console.log("The Token is invalid")
}
Output :
I'm implementing an web app that contains a chatbot that will remind the user on his upcoming google calendar events. I have successfully generated a jwt token when the user authorizes, but, I'm getting this error "JsonWebTokenError: invalid signature" when I verify the token. I'm still new to these concepts so I would really appreciate any help.
Here is where I signed my token:
let iss = 'GoogleCalender'
let sub = 'example#gmail.com'
let aud = 'xxxxxxxxxxxxxx'
let exp = '24h'
let sighOptions = {
issuer: iss,
subject: sub,
audience: aud,
expiresIn: exp,
algorithm: "RS256"
}
app.get('/landingPage', (req, res) => {
const token = jwt.sign({ user: 'iman' }, privateKey , sighOptions);
res.cookie('token', token,{ httpOnly: true });
res.sendFile(path.join(__dirname, "./landingPage.html"));
});
And here is where I verify the token:
let verifyOptions = {
issuer: iss,
subject: sub,
audience: aud,
maxAge: exp,
algorithms: "RS256"
}
function verifyToken(req,res,next){
const baererHeader = req.headers['authorization']
if(typeof baererHeader !== 'undefined'){
const baerer = baererHeader.split(' ')
const baererToken = baerer[1]
req.token = baererToken
next()
}
else{
res.sendStatus(403)
}
}
app.post('/landingPage',verifyToken, express.json(),(req,res)=>{
token = req.token
jwt.verify(token, publicKey, verifyOptions, (err,authData) =>{
const calendar = google.calendar({version: 'v3' , auth:createConnection()});
const agent = new dfff.WebhookClient({
request : req,
response : res
})
if(err) {
console.log(err)
function welcome(agent){
agent.add("Hi, Im helen, Please log in so i can remind you on your upcoming events")
}
}
else{
function welcome(agent){
agent.add("Hi, I'm Rem. Please click on remind me button if you want to be reminded on your upcoming events!")
} )
});
Is there any thing I'm doing wrong??
It's good that you're using a pair of private and public keys. It's better to use asymmetric signing than symmetric.
In your code I can see that you're sending the JWT token in a httpOnly cookie, but then in the landingPage you read it from the Authorization header. Not sure how is that supposed to work. Are you sure you're sending the right JWT to the /landingPage endpoint?
If you want to use this JWT that you issued yourself to access a user's data in a Google Calendar then it will not work. To access this data you need an access token issued by Google. Have a look at Google's documentation to check how to obtain an access token from them which will allow you to call the calendar API. You can still use the token that you are creating as a way of protecting your own endpoints. So: the user will need your token to be able to call your endpoint, and then a token from Google will be used to call the calendar API.
I notice that the docs specify that I can create a token to expire up to 3600 seconds later[1] But I don't see how to do that with auth().createCustomToken ... I can manually do it with jsonwektoken, but it seems like this should be addressable directly with firebase-admin library.
Another question is, what is the secret I need to verify my own token generated in this way, the uid ?
index.js
// demo server generating custom auth for firebase
import Koa from 'koa'
import Koajwt from 'koa-jwt'
import Token from './token'
const app = new Koa()
// Custom 401 handling if you don't want to expose koa-jwt errors to users
app.use(function(ctx, next){
return next().catch((err) => {
if (401 == err.status) {
ctx.status = 401
ctx.body = 'Protected resource, use Authorization header to get access\n'
} else {
throw err
}
})
})
// Unprotected middleware
app.use(function(ctx, next){
if (ctx.url.match(/^\/login/)) {
// use router , post, https to securely send an id
const conf = {
uid: 'sample-user-uid',
claims: {
// Optional custom claims to include in the Security Rules auth / request.auth variables
appid: 'sample-app-uid'
}
}
ctx.body = {
token: Token.generateJWT(conf)
}
} else {
return next();
}
});
// Middleware below this line is only reached if JWT token is valid
app.use(Koajwt({ secret: 'shared-secret' }))
// Protected middleware
app.use(function(ctx){
if (ctx.url.match(/^\/api/)) {
ctx.body = 'protected\n'
}
})
app.listen(3000);
token.js
//import jwt from 'jsonwebtoken'
import FirebaseAdmin from 'firebase-admin'
import serviceAccount from 'demo-admin-firebase-adminsdk-$$$$-$$$$$$.json'
export default {
isInitialized: false,
init() {
FirebaseAdmin.credential.cert(serviceAccount)
isInitialized = true
},
/* generateJWTprimiative (payload, signature, conf) {
// like: jwt.sign({ data: 'foobar' }, 'secret', { expiresIn: '15m' })
jwt.sign(payload, signature, conf)
} */
generateJWT (conf) {
if(! this.isInitialized)
init()
FirebaseAdmin.auth().createCustomToken(conf.uid, conf.claims)
.then(token => {
return token
})
.catch(err => {
console.log('no token generate because', err)
})
}
}
[1] https://firebase.google.com/docs/auth/admin/create-custom-tokens
You can't change the token expiration. The docs you found includes the words:
Firebase tokens comply with the OpenID Connect JWT spec, which means
the following claims are reserved and cannot be specified within the
additional claims:
... exp ...
This is further backed up by inspecting the Firebase Admin SDK source code on GitHub.
In this section:
public createCustomToken(uid: string, developerClaims?: {[key: string]: any}): Promise<string> {
// .... cut for length ....
const header: JWTHeader = {
alg: ALGORITHM_RS256,
typ: 'JWT',
};
const iat = Math.floor(Date.now() / 1000);
const body: JWTBody = {
aud: FIREBASE_AUDIENCE,
iat,
exp: iat + ONE_HOUR_IN_SECONDS,
iss: account,
sub: account,
uid,
};
if (Object.keys(claims).length > 0) {
body.claims = claims;
}
// .... cut for length ....
You can see the exp property is hard coded to be iat + ONE_HOUR_IN_SECONDS where the constant is defined elsewhere in the code as 60 * 60...
If you want to customize the expiration time, you will HAVE to create your own token via a 3rd party JWT package.
To your 2nd question, a secret is typically stored in the server environment variables, and is a pre-set string or password. Technically you could use the UID as the secret, but that would be a TERRIBLE idea security wise - please don't do this. Your secret should be like your password, keep it secure and don't upload it with your source code to GitHub. You can read more about setting and retrieving environment variables in Firebase in these docs here
I'm trying to create JWT tokens in node.js for use with the REST api in firebase, but when I try to use them, I get the error "Error: Invalid claim 'kid' in auth header."
This is my code
http.createServer(function (req, res) {
var payload = {
uid: "bruh"
};
var token = jwt.sign(payload, sact["private_key"], {
algorithm: 'RS256',
issuer: sact["client_email"],
subject: sact["client_email"],
audience: 'https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit',
expiresIn: '3600s',
jwtid: sact["private_key_id"],
header: {
"kid": sact["private_key_id"]
}
});
res.writeHead(200);
res.end("It worked. (" + token + ")");
}).listen(port);
These are my requires
var http = require('http');
var jwt = require('jsonwebtoken');
Please use returnSecureToken: true, with correct Spellings
I hope it will solve the problem of Invalid claim 'kid' in the auth header.
This is an issue because you're generating a Firebase ID token, not an access token for the Firebase REST API.
To generate a REST API token I would use the legacy Firebase Token Generator library which still works perfectly well (but only generates REST tokens, not general purpose access tokens).
Note that your Firebase Database secret is now located under the gear icon in the top left of the console.
So I had this error and I've fixed it. Now here is the solution:
You'll need to retrieve the ID-token using an additional function. Here is the function you can use:
firebase.auth().currentUser.getIdToken(/* forceRefresh */ true).then(function(idToken) {
// Send token to your backend via HTTPS
// ...
}).catch(function(error) {
// Handle error
});
I implemented it somewhat like this:
//google OAuth login handler
const googleLoginHandler = () => {
const provider = new firebase.auth.GoogleAuthProvider();
firebase.auth()
.signInWithPopup(provider)
.then((result) => {
/** #type {firebase.auth.OAuthCredential} */
setgoogleAuthStatus(true)
// The signed-in user info.
const userId = result.user.uid;
const displayName = result.user.displayName;
const email = result.user.email;
//This is the function for getting the ID-Token
firebase.auth().currentUser.getIdToken(/* forceRefresh */ true).then((idToken) => {
// Send token to your backend via HTTPS
console.log(idToken)
}).catch((error) => {
// Handle error
console.log(error.message)
alert(error.message)
});
console.log(result)
}).catch((error) => {
console.log(error)
// Handle Errors here.
alert(error.message)
})
}
The id token you get by this method can be used to access the firebase real-time database and other firebase services.
check out these links for more details:
https://firebase.google.com/docs/auth/admin/verify-id-tokens#retrieve_id_tokens_on_clients
https://firebase.google.com/docs/database/rest/auth#firebase_id_tokens
I am using google api nodejs , I try to get data from google Anaytics
var google = require('googleapis');
var OAuth2Client = google.auth.OAuth2;
var CLIENT_ID = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
var CLIENT_SECRET = 'xxxxxxxxxxxxxxxxxxxxxx';
//var REDIRECT_URL = 'http://yieldops.co/oauth2Callback';
var REDIRECT_URL = 'http://localhost:8000/oauth2Callback';
var oauth2Client = new OAuth2Client(CLIENT_ID, CLIENT_SECRET,REDIRECT_URL);
var credentials={}
credentials.expiry_date= "1463514280558",
credentials['refresh_token']="aaaaaaaaa"
credentials['access_token']="aaaaaaaaaaaaaaa"
oauth2Client.setCredentials(credentials)
var gauth = {
'auth': outh2Client,
'ids': 'ga:101' ,
'start-date': '2016-01-01',
'end-date': 'today',
'metrics': 'ga:impressions,ga:sessions,ga:pageviews',
'dimensions': 'ga:date'
}
analytics.data.ga.get(gauth, function (err, gaData) {
console.log("err 1234",err)
console.log("gaData ",gaData)
//console.log("ga",Object.keys(gaData))
})
Note
Now the problem is if access token is not expire then it give me data , and if access token is expire then it gave me 400 error Invalid grant . And if I remove expiry_date from credentials then it gave me error
{ [Error: Invalid Credentials]
code: 401,
errors:
[ { domain: 'global',
reason: 'authError',
message: 'Invalid Credentials',
locationType: 'header',
location: 'Authorization' } ] }
access_token have expire time is 1 hour. You must refresh access_token when it's expired.
oauth2Client.refreshAccessToken(function(err, tokens) {
// your access_token is now refreshed and stored in oauth2Client
// store these new tokens in a safe place (e.g. database)
});
Can you find it here
No need to explicitly refresh token, when access_token expired. Oauth2 Client will refresh token automatically and the request is replayed, provided when you get refresh_token in first authorization, set this refresh_token in OAuth2 client credentials as shown in the following code
const {tokens} = await oauth2Client.getToken(code)
oauth2Client.setCredentials(tokens);
Still, you want to manually refresh the access_token, then use the following code.
oauth2Client.on('tokens', (tokens) => {
if (tokens.refresh_token) {
// store the refresh_token in my database!
console.log(tokens.refresh_token);
}
});
OR
oauth2Client.refreshAccessToken((err, tokens) => {
// your access_token is now refreshed and stored in oauth2Client
// store these new tokens in a safe place (e.g. database)
});
References:
https://github.com/googleapis/google-api-nodejs-client/blob/c00d1892fe70d7ebf934bcebe3e8a5036c62440c/README.md#making-authenticated-requests
https://github.com/googleapis/google-api-nodejs-client/blob/c00d1892fe70d7ebf934bcebe3e8a5036c62440c/README.md#manually-refreshing-access-token
https://github.com/googleapis/google-api-nodejs-client#handling-refresh-tokens