How to login through a keycloak service account? - node.js

I can't figure out how to prepare the POST command to access keycloak with a service account.
I'm using nodejs to access a clean keycloak 4.8.1 server and tried a few configurations of POST but couldn't have success in log in through service account yet.
I'm having a hard time trying to simply login to a client through service account. I've tried some configurations but never got success in log in. The last code I've tried was like this:
function serviceAccountAuthenticate (client) {
return function login (realmName, secret) {
const clientSecret = `procempa-admin:${secret}`;
const basicToken = `Basic: ${btoa(clientSecret)}`;
return new Promise((resolve, reject) => {
const req = {
form: {'grant_type': 'client_credentials'},
authorization: basicToken,
'Content-Type': 'application/x-www-form-urlencoded',
};
const url = `${client.baseUrl}/realms/${realmName}/protocol/openid-connect/token`;
request.post(url, req, (err, resp, body) => {
if (err) {
return reject(err);
}
// Check that the status cod
if (resp.statusCode !== 200) {
return reject(body);
}
return resolve(body);
});
});
};
}
and the result was sad like this:
{ form: { grant_type: 'client_credentials' },
authorization: 'Basic: cHJvY2VtcGEtYWRtaW46YzZmZmUxNzUtNDgyZS00NjMxLWE5YjEtMTBjNWIyNjZlYzZi',
'Content-Type': 'application/x-www-form-urlencoded' }
Error {"error":"unauthorized_client","error_description":"INVALID_CREDENTIALS: Invalid client credentials"}

Related

How to get auth token for PowerBI REST API?

I am in the process of building a server-side app that is going to push the data into PowerBI's dataset. The only thing I'm struggling with is getting the bearer token to make a request to their API.
I'm getting the access token in the following way, however, I'm getting "401 Unauthorized" when using it in the request.
async getToken() {
const AuthenticationContext = adal.AuthenticationContext;
const context = new AuthenticationContext(`${ CONFIG.power_bi.authorityURI }/${ CONFIG.power_bi.tenantID }`);
return new Promise((resolve, reject) => {
context.acquireTokenWithClientCredentials(
CONFIG.power_bi.scope,
CONFIG.power_bi.clientID,
CONFIG.power_bi.clientSecret,
(error, tokenResponse) => {
if (error) {
console.log('PowerBI Token Acquire Error:', error);
return reject(tokenResponse == null ? error : tokenResponse);
}
console.log('PowerBI Token Acquired:', tokenResponse);
return resolve(tokenResponse);
}
);
});
}
async postRowsInDatasetTable() {
const token = await this.getToken().then(({ accessToken }) => accessToken);
const groupId = '{groupId}';
const datasetId = '{datasetId}';
const tableName = '{tableName}';
try {
const result = await axios.post(
`https://api.powerbi.com/v1.0/myorg/groups/${groupId}/datasets/${datasetId}/tables/${tableName}/rows`,
{
rows: [{
"status": "Deal",
"date": "2021-02-10T10:55:01.706Z"
}]
},
{
headers: {
'Authorization': `Bearer ${token}`,
'Content-type': 'application/json'
}
}
);
} catch (error) {
console.log(error);
}
}
The thing is that data push functionality is working just fine with a token which I took from PowerBI's documentation page, where they provide example API calls, as it is populated automatically based on currently logged-in user (e.g. https://learn.microsoft.com/en-us/rest/api/power-bi/pushdatasets/datasets_postrowsingroup), which means I am simply getting the wrong token.
Could anyone please advise how to get the appropriate token, as I found nothing in the documentation?
Thanks

export function with promise in nodejs

I am trying to import a function from an external file I have, which contains a promise.
The resolution of the promise is what should be returned, but I get an error message:
The requested module './functions.js' does not provide an export named 'getOkapiToken'
The POST request works fine when I run it directly in the server, but I need it in a separate file as it will be used by several different files in the future.
This is also the first time I'm using promises, so I'm not sure I handled it properly (though I can't get any errors registered until I deal with the first one).
The functions.js file is built as follows:
import post from 'axios';
export function getOkapiToken(url, user, password) {
//Get username and password for API
const auth = {
"username": user,
"password": password
};
//Create headers for POST request
const options = {
method: 'post',
data: auth,
headers: {
'Content-Type': 'application/json',
'Content-Length': auth.length,
'X-Okapi-Tenant': 'diku'
}
};
//Make API post call
post(url+':9130/authn/login', options)
.then(response => {
return(response.headers['x-okapi-token'])
}).catch((err) => {
return(`There was an error 2: ${err}`)
})
}
And I try to import it as follows:
import { getOkapiToken } from './functions3.js'
import settings from './jsons/config.json';
let OkapiKey = await new Promise((resolve,reject) => {
//Call function to make API post call
let keytoken = getOkapiToken(settings.url,settings.userauth,settings.passauth)
console.log(`There was an error: ${keytoken}`)
if (keytoken.length == 201) {
resolve(keytoken)
} else {
reject('There was an error')
}
})
OkapiKey.then((data) => {
console.log(data)
})
.catch((err) => {
console.error('I have an error:'+err.code);
})
There are three ways to handle asynchronous task in Javascript and wit Node.JS
Pass in a Callback to run in the asynchronous code
Use a Promise that will either resolve or reject the promise
Use the async keyword in front of the function and place await in front of the asynchronous code.
With that said I was able to get the code to work by running a simple node server and I modified your code just a bit to get a response back from the server.
index.js
const { getOkapiToken } = require('./functions.js')
const settings = require('./settings.json')
var OkapiKey = getOkapiToken(settings.url,settings.userauth,settings.passauth)
OkapiKey
.then((data) => {
console.log('I have data'+ data.toString())
})
.catch((err) => {
console.error('I have an error:'+err.code);
})
functions.js
const post = require('axios');
const getOkapiToken = (url, user, password) =>
new Promise(function (resolve, reject) {
//Get username and password for API
const auth = {
"username": user,
"password": password
};
//Create headers for POST request
const options = {
method: 'post',
data: auth,
headers: {
'Content-Type': 'application/json',
'Content-Length': auth.length,
'X-Okapi-Tenant': 'diku'
}
};
post('http://localhost:3000/', options)
.then(response => {
resolve(response.data)
// if (response.headers['x-okapi-token']) {
// resolve(response.headers['x-okapi-token']);
// } else {
// reject((error));
// }
}).catch((err) => {
console.error('Response Error:'+err)
})
})
exports.getOkapiToken = getOkapiToken;

How do I store JWT Token after receiving from Cognito? Logging in via the Cognito Hosted UI

Architecture: front end Angular, backend nodejs/express.
Currently the setup works as follow:
Login to the site via the Cognito Hosted UI
This redirects to our home page and sends us a code in the URL
I pull down this code in Angular
import { Component, OnInit } from '#angular/core';
import { DbService } from '../db.service';
import { Iss } from '../db.service';
import { Router, ActivatedRoute } from '#angular/router';
import { Http, Response, RequestOptions, Headers} from '#angular/http';
#Component({
selector: 'app-dashboard'
})
export class GroupSelectionComponent implements OnInit {
cognitoCode: string;
constructor(
private DbService: DbService,
private route: ActivatedRoute,
private router: Router
) {}
ngOnInit() {
this.route.queryParams
.subscribe(params => {
console.log(params);
console.log(params.code);
this.cognitoCode = params.code;
});
this.DbService.getIss(this.cognitoCode).subscribe(
iss => this.iss = iss
);
}
In the code you will see I am passing the congitocode to the dbservice for getIss.
db.service
getIss(cognitoCode ): Observable<Issuer[]> {
const url = hosturl +'i_l';
// let header: HttpHeaders = new HttpHeaders();
const httpOptions = {
headers: new HttpHeaders({
'Access-Control-Allow-Origin': '*',
'Content-Type': 'application/json',
'Authorization': cognitoCode
})
};
let params = new HttpParams()
console.log(httpOptions.headers);
return this._http.get(url, httpOptions)
.pipe(
map((res) => {
console.log(res);
return <Issuer[]> res;
})
);
}
I then send the code as part of the headers of my GET request to the backend.
The GET then hits my backend router with these settings.
var authMiddleware = require('../middleware/AuthMiddleware.js');
router.get('/i_l', authMiddleware.Validate, i_l.get);
This will then call my authMiddleware which takes the code provided by Cognito Hosted UI and use a POST against oauth2/token to get my JWT token.
That token is then parsed used to compare to the https://cognito-idp.us-east-2.amazonaws.com/REMOVED/.well-known/jwks.json for congnito.
Once validated the request continues and I get data back from the backend.
// POST that trades the code for a token with cognito
var options = {
'method': 'POST',
'url': 'https://REMOVED.amazoncognito.com/oauth2/token',
'headers': {
'Content-Type': 'application/x-www-form-urlencoded'
},
form: {
'grant_type': 'authorization_code',
'client_id': 'CLIENTIDREMOVED',
'code': req.headers['authorization'],
'redirect_uri': 'http://localhost/group-selection'
}
};
// First request gets the JSON request of the token using the POST above
request(options, function (error, response) {
if (error) throw new Error(error);
token = JSON.parse(response.body).access_token;
//localStorage.setItem('token', token);
// request pull down status based on validitiy of token
request({
url : `https://cognito-idp.us-east-2.amazonaws.com/REMOVED/.well-known/jwks.json`,
json : true
}, function(error, response, body){
console.log('token: ' + token);
if (!error && response.statusCode === 200) {
pems = {};
var keys = body['keys'];
for(var i = 0; i < keys.length; i++) {
var key_id = keys[i].kid;
var modulus = keys[i].n;
var exponent = keys[i].e;
var key_type = keys[i].kty;
var jwk = { kty: key_type, n: modulus, e: exponent};
var pem = jwkToPem(jwk);
pems[key_id] = pem;
}
var decodedJwt = jwt.decode(token, {complete: true});
if (!decodedJwt) {
console.log("Not a valid JWT token");
res.status(401);
return res.send("Not a valid JWT token");
}
var kid = decodedJwt.header.kid;
var pem = pems[kid];
if (!pem) {
console.log('Invalid token - decodedJwt.header.kid');
res.status(401);
return res.send("Invalid token - decodedJwt.header.kid");
}
jwt.verify(token, pem, function(err, payload) {
if(err) {
console.log("Invalid Token - verify");
res.status(401);
return res.send("Invalid token - verify");
} else {
console.log("Valid Token.");
return next();
}
});
} else {
console.log("Error! Unable to download JWKs");
res.status(500);
return res.send("Error! Unable to download JWKs");
}
});
});
Quesiton -- how I set this up so that the Token I get back continues for the user?
If I understand your question properly then you are trying to validate all your apis through cognito user right?
Then you just need to do two things.
Add in header JWT token once you are getting after login. Just store into your application scope and pass everytime whenever any API is calling.
Auth.signIn(data.username, data.password)
.then(user => {
let jwkToken = user.getSignInUserSession().getAccessToken().getJwtToken();
// Store above value in singletone object or application scope.
})
.catch(err => {
//error
});
Now When API is calling pass jwkToken as header.
Then Go AWS ApiGateWay Console and add into Authorizers.

Firebase cloud messaging: Request contains an invalid argument

I'm following this tutorial, I got to the point to send an HTTP request to the fcm endpoint, but I'm getting the following error:
{
error:
{
code: 400,
message: 'Request contains an invalid argument.',
status: 'INVALID_ARGUMENT'
}
}
Instead of sending the request using curl I'm using a cloud function using node.js | express with the following code:
exports.messages = function (req, res) {
const api = 'https://fcm.googleapis.com/v1/projects/myproject-b5ae1/messages:send';
const headers = {
'Accept': 'application/json',
'Content-type': 'application/json',
};
return getAccessToken().then((accessToken) => {
headers.Authorization = `Bearer ${accessToken}` // <-- accessToken is OK
const { title, body, token } = req.body;
return fetch(api, {
headers,
method: 'POST',
body: JSON.stringify(
{
'message': {
'token': token, // <-- this is the client fcm token
'notification': {
'title': title,
'body': body
}
}
}
)
}).then(res => res.json()).then(data => {
console.log(data);
res.sendStatus(200);
}).catch((error) => {
console.error(error);
});
});
}
Where the token is an OAuth 2 token for my service account
function getAccessToken() {
return new Promise(function (resolve, reject) {
var key = require('../keys/service-account.json');
var jwtClient = new google.auth.JWT(
key.client_email,
null,
key.private_key,
[
'https://www.googleapis.com/auth/firebase',
'https://www.googleapis.com/auth/firebase.database',
'https://www.googleapis.com/auth/firebase.messaging',
],
null
);
jwtClient.authorize(function (err, tokens) {
if (err) {
reject(err);
return;
}
resolve(tokens.access_token);
});
});
}
what am I missing here? any advice will be appreciated
I just realized I copied the uri for the fcm endpoint from the tutorial. I changed it to:
const api = 'https://fcm.googleapis.com/v1/projects/{project-Id}/messages:send';
and it worked!

how to authenticate to oracle cloud ipnetwork API using auth token?

I am unable to authenticate to oracle cloud using auth token.I am using "request" node module in node js to connect to oracle cloud using its REST endpoint.I am passing the authentication token in header and the response i am getting is"HTTP 401 Unauthorised".Dont know why it is happening.Any help is appreciated.
Here's an example that first obtains a token and then uses it for a subsequent request.
Start by setting these environment variables:
OC_REST_ENDPOINT
OC_IDENTITY_DOMAIN
OC_USER
OC_PASSWORD
For example:
export OC_REST_ENDPOINT=https://api-z999.compute.us0.oraclecloud.com/
export OC_IDENTITY_DOMAIN=myIdentityDomain
export OC_USER=some.user
export OC_PASSWORD=supersecretpassword
Then use the following example:
const request = require('request');
const restEndpoint = process.env.OC_REST_ENDPOINT;
const identityDomain = process.env.OC_IDENTITY_DOMAIN;
const user = process.env.OC_USER;
const password = process.env.OC_PASSWORD;
request(
{
method: 'POST',
uri: restEndpoint + 'authenticate/',
headers: {
'content-type': 'application/oracle-compute-v3+json',
},
body: JSON.stringify({ // Must be a string, buffer or read stream
user: '/Compute-' + identityDomain + '/' + user,
password: password
})
},
function(err, res, body) {
if (err) {
console.log(err);
return;
}
if (res.statusCode !== 204) {
console.log('Something broke.');
return;
}
console.log('Got auth token');
let token = res.headers['set-cookie'][0];
request(
{
method: 'GET',
uri: restEndpoint + 'instance/',
headers: {
'accept': 'application/oracle-compute-v3+directory+json',
'cookie': token
}
},
function(err, res, body) {
if (err) {
console.log(err);
return;
}
console.log(body);
}
);
}
);

Resources