Pass JWT to rabbitmq over websockets - javascript

This is my current RabbitMQ / Stomp setup which works fine with username and password authentication.
My end goal is to pass a JWT instead of username and password. I already have access to the token on the front-end and I do not want to conduct a UAA server. I have added this plugin to RabbitMQ and tried doing what step 3 of the Authorization flow says with no luck.
Client passes the token as password when connecting to a RabbitMQ
node. The username field is ignored.
I get the following error:
<<< ERROR
content-length:16
version:1.0,1.1,1.2
content-type:text/plain
message:Processing error
content-length:16
Edit
Another observation is that after adding "Authorization: Bearer" in front of the token I get the a new error.
<<< ERROR
content-length:33
version:1.0,1.1,1.2
content-type:text/plain
message:Bad CONNECT
content-length:33
Access refused for user 'rabbit'
RabbitMQ Setup
Docker
FROM rabbitmq
RUN rabbitmq-plugins enable --offline rabbitmq_management
RUN rabbitmq-plugins enable --offline rabbitmq_stomp
RUN rabbitmq-plugins enable --offline rabbitmq_web_stomp
RUN rabbitmq-plugins enable --offline rabbitmq_auth_backend_oauth2
COPY ./advanced.config /etc/rabbitmq/advanced.config
COPY ./rabbitmq.conf /etc/rabbitmq/rabbitmq.conf
EXPOSE 15671 15672 15674 61613
advanced.config
[
{rabbit, [
{default_user, <<"admin">>},
{default_pass, <<"guest">>},
{auth_backends, [rabbit_auth_backend_oauth2, rabbit_auth_backend_internal]}
]},
{rabbitmq_management, [
{enable_uaa, false}
]},
{rabbitmq_auth_backend_oauth2, [
{resource_server_id, <<"rabbitmq">>},
{key_config, [
{default_key, <<"key-1">>},
{signing_keys,
#{<<"key-1">> => {pem, <<"-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAr8xZBLjW7yMZJobc/IFo
6New9jlCvSx8I6aSBrU+EU+TN6iXF13Tqqp62L/TWlTrYlErsu2yvLknhN4VU7qX
g38/2wZn9/kKFYCkeaDe3lDxl+4rNGq1TSEXCzndIDfVzgxZvKZ0Vt5+mvr1cxR7
7V6MFcJvIHMv4MdIqAUwABFFHvonp56yzh7g/UsAk6XlaqhoZQ1NrPyWpG6YgHwg
hFibyh0ZI3gD7H0UshpR9VVPLTgrAUnf3qWMpEXKO2ePmrTVwwDpL25n5DdhaBz6
e+YSonTT7KigxeklRPrKc7km29l0frml+8jOsQsx7L2WCD/HqR6J5iVDZ5mDfeKN
vcixi+nkLTCyV1jsHEzteSmGgmAE6SrSMXV+oPz2xLGKEGJJQWS+Ll1l5BD1MmZE
6DqLoe8FHew54M7zniCxkhDIHcpVY3IYcv2gohhaxm9MHkSHaj6bwAGQcORwgGho
ETVr7RXyvCD5Vsr6VqDqknjleP1tLi/gnlzP099SLmGiB6y/v1mW1s3tDpI/F7b7
WWDyHRqW9YfGEPYOmNdELcizW9UxY2MFvK4LlacRaXzzDEXmiP/FK84Qse14Nmyi
6tfqIYe+PRWbA7ztOaBlmDnkhxHcLufB48t4dleUWmj+6VOXQGXef10p4rQNeOo5
N5dJoYqveUJ6Lfln3C25NJcCAwEAAQ==
-----END PUBLIC KEY-----">>}
}
}]
}
]}
].
rabbitmq.conf
loopback_users.guest = false
total_memory_available_override_value = 2684354560
listeners.tcp.default = 5672
management.tcp.port = 15672
web_stomp.ws_frame = binary
Current Stomp client setup
var client = new StompJs.Client({
brokerURL: 'ws://localhost:5900/ws',
connectHeaders: {
login: 'admin',
passcode: 'guest'
},
debug: log => (
console.log(
log
)
),
reconnectDelay: 5 * 1000,
heartbeatIncoming: 4 * 1000,
heartbeatOutgoing: 4 * 1000,
});
Ideal JWT setup
var client = new StompJs.Client({
brokerURL: 'ws://localhost:5900/ws',
connectHeaders: {
login: 'rabbit',
passcode: 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc0FjdGl2ZSI6dHJ1ZSwiX2lkIjoiNWZkNzc0MDY4NzE3NTU2OWFlMTIxNmRmIiwiY29kZSI6IjIwMDAwMDEwNiIsInVzZXJuYW1lIjoiYW50b25pc2NvZGV1cCIsImVtYWlsIjoiYW50b25pc0Bjb2RldXAuc29mdHdhcmUiLCJmaXJzdE5hbWUiOiJBbnRvbmlzIiwibGFzdE5hbWUiOiJQaXNoaWFzIiwib3JnYW5pc2F0aW9uIjoiQ29kZVVwIFNvZnR3YXJlIEx0ZCIsInJvbGUiOnsiX2lkIjoiNWZkMmEyM2VjOTJmOTFjNTFkOTIzMTZiIiwibmFtZSI6ImN1c3RvbWVyIiwicmVhZCI6W3siX2lkIjoiNWZkMmEyM2VjOTJmOTFjNTFkOTIzMTViIiwibmFtZSI6ImFjY291bnRzIn0seyJfaWQiOiI1ZmQyYTIzZWM5MmY5MWM1MWQ5MjMxNWMiLCJuYW1lIjoiY3JtIn0seyJfaWQiOiI1ZmQyYTIzZWM5MmY5MWM1MWQ5MjMxNWQiLCJuYW1lIjoid2FyZWhvdXNlIn1dLCJjcmVhdGUiOltdLCJ1cGRhdGUiOltdLCJtb3ZlIjpbXSwiX192IjowfSwiX192Ijo3LCJzY29wZXMiOlsicmFiYml0bXEuY29uZmlndXJlOiovKiIsInJhYmJpdG1xLndyaXRlOiovKiIsInJhYmJpdG1xLnJlYWQ6Ki8qIl0sImlhdCI6MTYwNzk1NjkzNCwiZXhwIjoxNjA4NTYxNzM0LCJhdWQiOlsiaHR0cHM6Ly9iaXpjb3VyaWVyLmV1IiwicmFiYml0bXEiXSwiaXNzIjoiaHR0cHM6Ly9yZXN0LmJpemNvdXJpZXIuZXUifQ.Ypqa16YuqH3rfwxAQZ_RdJRF21h6jYX07r-zaB3AP_qshU_xBiP9RpFvFkws13i0_X3Ou-qKxSdAF51T1t-Ob5V4LiXLJKsesmZIeIGXs-Dhc79u5UqhOdGIzPgRTlieOIUmrFKsPBWf7FnoR-lgd9gH8ge3Wp2f2vOqU24JsEjkvtlTGLS1aId27UVz0d2qxMLWf-9kLxW4mmuM2UCeRsnznpPaZbAfhq2tHHIy3EtGeIIiOsAE0lM9BfJRF36kjZcpcMZSnhTrkZjiDsrUuiVd-W27MVJRYFVpULkGrpa5RkkDG5ZbwNNSn0VUMZB2PiQTchtNyoffutPzhktxieDO5T0OzrSXl5VAtgZFHi5oPLWtPGVejNiWMFsY9u1ale6NRDSReeQjPPEqzpH-tfnugkwyv_jhL6WnfmoeEBgcuyK7eBvrg7uJbQFdwMrRQXkGn_ATn0XhJWI-Hia3muNbNbvdL0jgkZBv0HFPsx84n3-IEx672_6Wy2qtLtFcnUgGr4ThRVp8PE0ro21JdvjFbwgq9k5Qhv5MA6wgB6nt7vtuP1SZ17ekIPE6izwHUPZsWsqJg6v108H1B13J2yxK5LcdsCb8Z8uRhiRSKt4Cs6MHsZg0GCOjym2YHXyN21Wpw3behq0GxUiSrU4qkADujDb6nNzaLq2_ecn5bOI'
},
debug: log => (
console.log(
log
)
),
reconnectDelay: 5 * 1000,
heartbeatIncoming: 4 * 1000,
heartbeatOutgoing: 4 * 1000,
});
You can decode the JWT here

I think that you are missing a scope in your JWT
See the documentation here:
https://github.com/rabbitmq/rabbitmq-server/tree/master/deps/rabbitmq_auth_backend_oauth2
It says:
Token scope returned by OAuth 2.0 provider must include RabbitMQ resource scopes that follow a convention used by this plugin: configure:%2F/foo means "configure permissions for 'foo' in vhost '/'") (scope field can be changed using extra_scopes_source in advanced.config file.
(BTW: I coudn't find the "extra_scopes_source" in your advanced.config as well)
Although I didn't test it, failing to provide the scope would logically result in an access denied error.

Related

Why all users in Strapi have access to update all users profile?

I added a new filed called config (type: json) to the User model. I use built-in swagger of Strapi local document. The problem is that I can update another user config (data) with put method.
First, I authorized by POST /auth/local and get my token and my user id (in this cast it's 5)
I add my token to swagger Authorize button.
Then, I use PUT /user/{id} in this case id is 5.
Calling api http://localhost:1337/api/users/4 returns 200!
I expect that I get 403 error! Because I should not able to change other user profiles!!!
Is it normal? If yes, tell me a solution to fix this.
This is because Strapi has only two default roles:
Public
Authenticated
So by default, when you setup permissions, whatever authentication state currently the user has access to all the content accordingly (e.g. Public to only public, Authenticated to authenticated)
To work with this, and to limit the user actions in the auth scope you have to use middleware or policy, so since this is in user-permissions scope let's add policy to user-permissions:
Strapi 4.5.3
yarn strapi generate
? Strapi Generatos
>policy
? Policy name
isOwner
? Where do you want to add this policy?
> Add policy to root of project
Next step is in your /src/extensions folder you have to create folder users-permissions, and in this folder file strapi-server.js with following content:
/src/extensions/users-permissions/strapi-server.js
module.exports = (plugin) => {
for (let i = 0; i < plugin.routes["content-api"].routes.length; i++) {
const route = plugin.routes["content-api"].routes[i];
if (
route.method === "GET" &&
route.path === "/users/:id" &&
route.handler === "user.findOne"
) {
console.log(route);
plugin.routes["content-api"].routes[i] = {
...route,
config: {
...route.config,
policies: route.config.policies
? [...route.config.policies, "global::isOwner"] // tests if policies were defined
: ["global::isOwner"],
},
};
}
}
return plugin;
};
if you did the step correct in your strapi server console you have to see:
info: In isOwner policy. if you send get request to /api/users/:id
Next step is we are going to modify policy file like so:
/src/policies/isOwner.js
"use strict";
/**
* `isOwner` policy
*/
module.exports = async (policyContext, config, { strapi }) => {
strapi.log.info("In isOwner policy.");
const { user, auth } = policyContext.state;
const { params } = policyContext;
// this case the userId is the same as the id we are requesting
// other cases would need more extensive validation...
const canDoSomething = user.id == params.id;
if (canDoSomething) {
return true;
}
return false;
};
and whoala:
{
"data": null,
"error": {
"status": 403,
"name": "PolicyError",
"message": "Policy Failed",
"details": {}
}
}
if we try to get other user profile

why messaging().sendtodevice is not working sometimes?

I'm using the following code to send a notification from one device to another using FCM. Everything works fine until before return admin.messaging().sendToDevice(...). The 'Token ID: ' log displays token ID of the receiver, but when I set the variable token_id to the sendToDevice function, the notification is not called, therefore the notification is not sent. Can someone tell me what's wrong?
var firebase = require("firebase-admin");
var serviceAccount = require("./julla-tutorial.json");
console.log("enter in then Firebase Api");
const firebaseToken = [
'e0T6j1AiRjaa7IXweJniJq:APA91bHNznSHSIey08s-C-c3gchci6wepvhP1QxQyYbmZ8LySI3wnu64iW7Q23GhA6VCdc4yodZoCFOgynfAb5C8O8VE81OcSv_LL-K3ET1IKGZ_6h35n-_q5EKFtfJWlzOqZr4IvpiB',
'dNWnSqyCQbufzv1JutNEWr:APA91bFcI9FDyRxHRBEcdw4791X0e-V0k1FjXcSstUA67l94hSojMRCd6LWr2b57azNEt3z_XLwLljMX4u2mc9cZDrAVm55Mw9CHGyue-09KofWnnHNR9XWBibc4T76xOV_DWX7T2RvW',
'cq65rtuaTCKGk5lHk7UabN:APA91bFR3kAArg6lhuBq7ktNuBk7Z9MXXk3PskqhYa8CgNaEl6MX4TQ5lo35d6XhnCQ4fEkCkyZ_j08evxE9Y4oVCRTEdqsrkccCVTE8Di47lfmDR3i1NdoL3re9oLw6F_uNsnvRoQcq'
]
firebase.initializeApp({
credential: firebase.credential.cert(serviceAccount)
})
const payload = {
notification: {
title: 'Demo 2345',
body: 'dfghj',
sound: 'default',
color: 'yellow',
android_channel_id: 'default',
channel_id: 'default'
},
data: { id: 'broadcast', channelId: 'default' }
}
const options = {
priority: 'high',
timeToLive: 60 * 60 * 24, // 1 day
};
console.log('------payload---',payload);
console.log('-----TOKEN_Array----',firebaseToken);
console.log('-------options-----',options);
firebase.messaging().sendToDevice(firebaseToken, payload, options).then(function (response) {
console.log('--------response',response);
}) .catch(function (error) {
console.log('-------rejet',reject);
});
It looks like you did not change the code from this tutorial:
https://medium.com/#jullainc/firebase-push-notifications-to-mobile-devices-using-nodejs-7d514e10dd4
you will need to change the 2nd line of code:
var serviceAccount = require("./julla-tutorial.json");
to actually point to your own firebase-push-admin.json file which holds your private keys registering your backend app with the firebase cloud messaging api. you can download this file from the firebase console as mentioned in the above article.
I recommend hiding this file from your git history by adding it to .gitignore so you dont accidentally push your private keys to a public repo.
I will link you another resource in addition to above link which helped me implement firebase push notifications in a nodeJS backend app.
https://izaanjahangir.medium.com/setting-schedule-push-notification-using-node-js-and-mongodb-95f73c00fc2e
https://github.com/izaanjahangir/schedule-push-notification-nodejs
Further I will also link you another repo where I am currently working on a fully functional firebase push notification implementation. Maybe it helps to actually see some example code.
https://gitlab.com/fiehra/plants-backend

MS graph API: 400 AuthenticationError with "/me/onlineMeetings" request

I am trying to create an online meeting and recover its URL like explained here in the docs, but when the request is run I get this error:
{
"statusCode": 400,
"code": "AuthenticationError",
"message": "Error authenticating with resource",
"requestId": "652ea3be-6a97-47e8-bfc6-3d7d1d51d425",
"date": "2020-09-01T12:53:41.000Z",
"body": "{
"code":"AuthenticationError",
"message":"Error authenticating with resource",
"innerError":{
"date":"2020-09-01T13:53:41",
"request-id":"652ea3be-6a97-47e8-bfc6-3d7d1d51d425"
}
}"
}
I tried also the get started projet for JS and it's working fine so I can't spot the problem.
here is what I used:
const msalConfig = {
auth: {
clientId: 'my_app_id',
redirectUri: 'http://localhost:8080'
},
cache: {
cacheLocation: "sessionStorage",
storeAuthStateInCookie: false,
forceRefresh: false
}
};
const loginRequest = { scopes: [
'openid',
'profile',
'user.read',
'calendars.read',
'User.Read.All',
'User.Export.All'
]
}
const options = new MicrosoftGraph.MSALAuthenticationProviderOptions([
'user.read',
'calendars.read',
'OnlineMeetings.ReadWrite'
]);
const onlineMeeting = {
startDateTime:"2020-09-01T16:00:34.2444915-07:00",
endDateTime:"2020-09-01T16:30:34.2464912-07:00",
subject:"test meeting"
};
const authProvider = new MicrosoftGraph.ImplicitMSALAuthenticationProvider(msalClient, options);
// Initialize the Graph client
const graphClient = MicrosoftGraph.Client.initWithMiddleware({authProvider});
// then I call this inside an async function
let events = await graphClient.api('/users/my_UserPrincipalName/onlineMeetings').post(onlineMeeting);
//let events = await graphClient.api('/me/onlineMeetings').post(onlineMeeting);
// I tried with both calls and none of them worked
and here are the permissions on azure active directory:
So any ideas on how to solve this ?
thanks
You didn't provide a correct access token.
Since Create onlineMeeting only supports Delegated (work or school account) permission type, you need to get the access token with Auth code flow or Implicit flow.
The started project for JS is using Implicit flow. So you can use Implicit flow to get the access token.
Here is the example in Postman:
The Auth URL above is https://login.microsoftonline.com/{your tenant}/oauth2/v2.0/authorize.
I figured out how to make it work in my code:
let's call my user, which I used all this time, user "A", all I did is that I simply created another user "B" in Azure Active Directory and then logging in with this new user "B" in the login screen instead of the admin user "A" that I used before..... and now it's working.
But this does not explain the issue, so if anyone can explain the difference or why it didn't work with the first account, that would be very helpful.

JavaScript syncronicity: Combining onAuthRequired Listener and native massaging

I have this problem ... and I've been trying to get a username and password pair passed to background.js of my extension. The process goes as follows:
A Credentials Server (CS) is started up with encrypted credential tokens
Selenium opens the browser with custom extension
The extension is triggered onAuthRequired
The Native Messaging Host (NMH) starts then receives a 'ready' message
The NMH uses a simple protocol to request tokens from the CS
The NMH decrypts the tokens
The NMH passes the credentials to background.js
background.js returns the credentials to chrome
Authentication magic happens on the remote server
I have solved everything right up to 6 ... maybe 7. The issue is that I am not able to actually capture what the NMH is sending.
It seems to be that either the credentials are not formatted correctly, thus they're being rejected or there's a problem with how the JavaScript side is put together and so it's just a case of broken code.
It's also possible that what I intend is not at all possible ... though I doubt that is the case.
I will provide as much code as I can ... and while I appreciate any help, I am happy to get a generic example which can be modified in my own time.
Got a better approach? I'm all ears! Happy to create a new question to address other ideas.
EDIT 1.0:
I should add that, when run, the alert at line 9 does popup.
When modified to read:
window.alert(String(response));
I get [object Object]
EDIT 2.0:
I changed window.alert(String(response)); to window.alert(JSON.stringify(response)); and it produced the expected outcome: {"username":"\"some_user\"","password":"\"1Password\"}
I expect this would translate to '{username:"some_user",password:"1Password"}'
There appears to be a problem passing the value of response to creds since the NMH fails to run at all once I try to access the username/password property of creds outside the callback.
This is probably a scope issue ...
Edit 3.0:
I found the issue - it's a synchronicity problem.
I've found many, many questions on this - I'll answer this when I've come up with a work around myself. Feel free to answer with a solution, should you have one.
Current background.js (not working)
chrome.webRequest.onAuthRequired.addListener(() => {
var hostName = "aardvark_nm_host";
var creds = null;
chrome.runtime.sendNativeMessage(
hostName,
{ text: "Ready" },
function(response){
window.alert("response");
console.log(response);
creds = JSON.parse(response);
}
);
return {authCredentials: creds};
},
{urls: ["<all_urls>"]},
['blocking']
);
Current aardvark_nm_host.py (Probably working)
#!/usr/bin/env python
from locksmith import decrypt_token, KEYFILE
import socket
import struct
import sys
import json
PORT = 9999
ADDR = 'localhost'
ERRMSG = {"username":"ERROR", "password":"password"}
def report_string(string):
def transceive(signal):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server = (ADDR, PORT)
sock.connect(server)
try:
sock.sendall(signal)
except Exception as e:
print >>sys.stderr, e
sock.close()
try:
sock.sendall(string)
sock.close()
except Exception as e:
print >>sys.stderr, e
sock.close()
transceive('S')
def get_tokens():
def recv_burst(sock):
total_data = []
while True:
data = sock.recv(1024)
if not data:
break
total_data.append(data)
return ''.join(total_data)
def transceive(signal):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server = (ADDR, PORT)
sock.connect(server)
try:
sock.sendall(signal)
message = recv_burst(sock)
except Exception as e:
report_string(e)
finally:
sock.close()
return message
try:
username = transceive('U')
username = decrypt_token(username)
report_string(username)
except Exception as e:
report_string(str(e))
username = "TOKEN_ERROR"
try:
password = transceive('P')
password = decrypt_token(password)
report_string(password)
except Exception as e:
report_string(str(e))
password = "TOKEN_ERROR"
return {"username":username, "password":password}
def read_message():
text_length = sys.stdin.read(4)
if len(text_length) == 0:
sys.exit(0)
text_length = struct.unpack('#I', text_length)[0]
text = sys.stdin.read(text_length)
return json.loads(text)
def encode_message(message):
encoded_content = json.dumps(message)
encoded_length = struct.pack('#I', len(encoded_content))
return {'length': encoded_length, 'content': encoded_content}
def send_message(encoded_message):
sys.stdout.write(encoded_message['length'])
sys.stdout.write(encoded_message['content'])
sys.stdout.flush()
def interpretation(message):
return message["text"] == "Ready"
while True:
received_message = read_message()
report_string(received_message)
if interpretation(received_message):
creds = get_tokens()
send_message(encode_message(creds))
creds = None
else:
send_message(encode_message(ERRMSG))
Current extension manifest.json
{
"version": "1.0.0",
"manifest_version": 2,
"name": "authvark",
"permissions": [
"<all_urls>",
"webRequest",
"webRequestBlocking",
"nativeMessaging"
],
"background": {
"scripts": ["background.js"],
"persistent": false
}
}
native-messaging-hosts/manifest.json
{
"name": "aardvark_nm_host",
"description": "Intermediary Credential Host",
"path": "/path/to/aardvark_nm_host.py",
"type": "stdio",
"allowed_origins": [
"chrome-extension://.../"
]
}
The solution to the problem was to use callbacks.
I thought I would have to deal with Promises, which I did a lot of work on ... only to find there was a much easier answer.
Much easier.
Providing credentials at the onAuthReauired event should work with the following script:
function callbackFn(details) {
return {
authCredentials: {
username: "%s",
password: "%s"
}
};
}
chrome.webRequest.onAuthRequired.addListener(
callbackFn,
{urls: ["<all_urls>"]},
['blocking']
);
resource
docs

Cannot find name 'require'. ANGULAR 2

I have an angular2 app where I need to use a payment API.
https://stripe.com/docs/quickstart
In this code sample on the nodejs section they say to use it like this:
// Set your secret key: remember to change this to your live secret key in production
// See your keys here: https://dashboard.stripe.com/account/apikeys
var stripe = require("stripe")("sk_test_BQokikJOvBiI2HlWgH4olfQ2");
// Token is created using Stripe.js or Checkout!
// Get the payment token submitted by the form:
var token = request.body.stripeToken; // Using Express
// Charge the user's card:
var charge = stripe.charges.create({
amount: 1000,
currency: "usd",
description: "Example charge",
source: token,
}, function(err, charge) {
// asynchronously called
});
We installed requireJS with NPM, however when we run it we get this error:
Cannot find name 'require'.
L48: pagar() {
L49: var stripe = require("stripe")("sk_test_ayzCMDmjq2QOIW0s3dTihxNR");
Try this instead:
import * as stripe from "stripe";
public stripeKey = stripe("sk_test_BQokikJOvBiI2HlWgH4olfQ2");

Categories

Resources