django channels custom token authenticated Websocket keeps disconnecting ERR_CONNECTION_RESET - javascript

i using react as my front end and django as my backend , as such , it requires me to use token based authentication with django channels. The method i am employing is by sending the authentication token as a cookie header. The methodology was taken from this git hub post here
So far , i have gotten most of the working parts together , however i don't seem to be able to persist the connection , it will always return an error in my console:
ERR_CONNECTION_RESET
Here is my code:
FRONTEND: Websocket.js
class WebSocketService{
static instance = null;
callbacks = {};
static getInstance(){
if (!WebSocketService.instance){
WebSocketService.instance = new WebSocketService();
}
return WebSocketService.instance;
}
constructor(){
this.socketRef = null;
}
connect(token){
var loc = window.location
var wsStart = 'ws://'
if (loc.protocol === 'https'){
wsStart = 'wss://'
}
const path = wsStart + 'localhost:8000'+ loc.pathname
// console.log(path)
// console.log(path + "?token=" + token)
document.cookie = 'authorization=' + token + ';'
console.log(document.cookie)
this.socketRef = new WebSocket(path)
this.socketRef.onmessage = e => {
console.log('in on message')
this.socketNewMessage(e.data);
};
this.socketRef.onopen = () => {
console.log(this.props.token)
console.log("WebSocket open");
};
this.socketRef.onerror = e => {
console.log('error happ')
console.log(e.message);
};
this.socketRef.onclose = () => {
console.log("WebSocket closed, restarting..");
this.connect(token);
};
}
socketNewMessage(data){
const parsedData = JSON.parse(data);
const command = parsedData.command;
if(Object.keys(this.callbacks).length === 0){
return;
}
if(command === 'messages'){
this.callbacks[command](parsedData.messages);
}
if(command === 'new_message'){
console.log("okay so this was called")
this.callbacks[command](parsedData.message);
}
}
state(){
return this.socketRef.readyState;
}
waitForSocketConnection(callback){
const socket = this.socketRef;
const recursion = this.waitForSocketConnection;
setTimeout(
function(){
if(socket.readyState === 1){
console.log("Connection is made");
if(callback != null){
callback();
}
return;
}
else{
console.log("Wait for connection..");
recursion(callback);
}
}, 1);
}
}
let WebSocketInstance = WebSocketService.getInstance();
export default WebSocketInstance;
FRONTEND: Apps.js
class App extends Component {
componentDidMount() {
console.log('app mounting..')
this.props.onTryAutoSignup();
console.log(this.props.isAuthenticated)
if (this.props.isAuthenticated) {
WebSocketInstance.connect()
}
}
componentDidUpdate(oldProps) {
console.log('app updating props..')
if (this.props.token !== oldProps.token ) {
console.log(this.props.token)
WebSocketInstance.connect(this.props.token)
}
}
render() {
return (
<div>
<Router>
<CustomLayout {...this.props}>
<BaseRouter/>
</CustomLayout>
</Router>
</div>
);
}
}
const mapStateToProps = state => {
return {
isAuthenticated: state.token !== null ,
token : state.token
}
}
const mapDispatchToProps = dispatch => {
return {
onTryAutoSignup: () => dispatch(actions.authCheckState())
}
}
export default connect(mapStateToProps, mapDispatchToProps)(App);
BACKEND: token_auth.py ( A custom middleware)
#database_sync_to_async
def get_user(token_key):
try:
return Token.objects.get(key=token_key).user
except Token.DoesNotExist:
return AnonymousUser()
class TokenAuthMiddleware:
"""
Token authorization middleware for Django Channels 2
see:
https://channels.readthedocs.io/en/latest/topics/authentication.html#custom-authentication
"""
def __init__(self, inner):
self.inner = inner
def __call__(self, scope):
return TokenAuthMiddlewareInstance(scope, self)
class TokenAuthMiddlewareInstance:
def __init__(self, scope, middleware):
self.middleware = middleware
self.scope = dict(scope)
self.inner = self.middleware.inner
async def __call__(self, receive, send):
headers = dict(self.scope["headers"])
print(headers[b"cookie"])
if b"authorization" in headers[b"cookie"]:
print('still good here')
cookies = headers[b"cookie"].decode()
token_key = re.search("authorization=(.*)(; )?", cookies).group(1)
if token_key:
self.scope["user"] = await get_user(token_key)
return self.inner(self.scope)
BACKEND : Router.py
application = ProtocolTypeRouter({
"websocket": TokenAuthMiddlewareStack(
URLRouter([
path("", NotificationConsumer),
]),
),
})
TokenAuthMiddlewareStack = lambda inner: TokenAuthMiddleware(AuthMiddlewareStack(inner))
Here is my the items that were printed out in my cmd prompt:
WebSocket HANDSHAKING / [127.0.0.1:59931]
b'authorization=xxxxxxxxxxxx' #<-- token from the cookie header
still good here #<--- passed first validation
user1 #<---- went into the get_user function i declared within my middleware
WebSocket DISCONNECT / [127.0.0.1:59931] #<---- disconnects...
Please help! Im lost!

the issue was with the middleware instance , it should be :
class TokenAuthMiddlewareInstance:
def __init__(self, scope, middleware):
self.middleware = middleware
self.scope = dict(scope)
self.inner = self.middleware.inner
async def __call__(self, receive, send):
close_old_connections()
headers = dict(self.scope["headers"])
print(headers[b"cookie"])
if b"authorization" in headers[b"cookie"]:
print('still good here')
cookies = headers[b"cookie"].decode()
token_key = re.search("authorization=(.*)(; )?", cookies).group(1)
if token_key:
self.scope["user"] = await get_user(token_key)
inner = self.inner(self.scope)
return await inner(receive, send)
TokenAuthMiddlewareStack = lambda inner: TokenAuthMiddleware(AuthMiddlewareStack(inner))
thanks!

Related

adal getCachedToken is always null

I would get cached token using react-adal api
import { authContext, } from '../auth'
const AvatarContainer = () => {
getPersonPhoto(authContext)
return (
<Avatar />
)
}
async function getPersonPhoto(authContext) {
const user = await authContext.getCachedUser();
const token = authContext.getCachedToken('https://graph.microsoft.com')
console.log("the token", token)
var photoUrl = "https://graph.microsoft.com/v1.0/users/" + user.profile.aud + "/photo/$value";
var Avatar = null;
// try {
// Avatar = await client
// .api(photoUrl)
// .get();
// return Avatar;
// } catch (e) { return null; }
}
export default AvatarContainer
The token value is always null, I have specified the ressource uribut it's not working.
I checked my localstorage I have the token
You might try opening an issue in Github, or upgrading to MSAL now that ADAL is being deprecated.

Export default not waiting my function return query. React Native

I want login system. My export default not working Read function. Read function query user_id in AsyncStorage. Please help me :)
app.js
var sendTabs = <TabsScreens />
var sendLogin = <LoginScreens />
var Read = readStore = async () => {
try {
const value = await AsyncStorage.getItem('user_id');
if (value !== null) {
return 1;
} else {
return 0;
}
} catch (e) {
alert(e);
}
}
var Welcome;
Read().then((response) => {
if (response == 1) {
Welcome = sendTabs
} else {
Welcome = sendLogin;
}
});
export default () => (Welcome)
You could define new component to handle this logic:
export function WelcomeScreen() {
const [isLoggedIn, setIsLoggedIn] = React.useState(null); // null | true | false
React.useEffect(() => {
void async function getUserId() {
const id = await AsyncStorage.getItem('user_id');
setIsLoggedIn(Boolean(id)); // true -> user_id found, false -> no valid user_id found
}();
}, []);
return (userId === null)
? <Text>Loading...</Text> // this will show until storage is checked
: (isLoggedIn) ? <TabsScreens /> : <LoginScreens />; // depending on the value of id from storage you show tabs or login screen
}

Always throws registration failed error while subscribing push notifications

I am working on solutions using which i can send desktop push notification to subscribed clients.
I have created basic solution in where whenever user click on button i ask user for whether they want to allow notifications for my app or not!
I am getting an error of "Registration failed - permission denied" whenever i click on button for first time.
So that i am not able to get required endpoints to save at backend
Here is my code
index.html
<html>
<head>
<title>PUSH NOT</title>
<script src="index.js"></script>
</head>
<body>
<button onclick="main()">Ask Permission</button>
</body>
</html>
index.js
const check = () => {
if (!("serviceWorker" in navigator)) {
throw new Error("No Service Worker support!");
} else {
console.log("service worker supported")
}
if (!("PushManager" in window)) {
throw new Error("No Push API Support!");
} else {
console.log("PushManager worker supported")
}
};
const registerServiceWorker = async () => {
const swRegistration = await navigator.serviceWorker.register("/service.js?"+Math.random());
return swRegistration;
};
const requestNotificationPermission = async () => {
const permission = await window.Notification.requestPermission();
// value of permission can be 'granted', 'default', 'denied'
// granted: user has accepted the request
// default: user has dismissed the notification permission popup by clicking on x
// denied: user has denied the request.
if (permission !== "granted") {
throw new Error("Permission not granted for Notification");
}
};
const main = async () => {
check();
const swRegistration = await registerServiceWorker();
const permission = await requestNotificationPermission();
};
// main(); we will not call main in the beginning.
service.js
// urlB64ToUint8Array is a magic function that will encode the base64 public key
// to Array buffer which is needed by the subscription option
const urlB64ToUint8Array = base64String => {
const padding = "=".repeat((4 - (base64String.length % 4)) % 4);
const base64 = (base64String + padding)
.replace(/\-/g, "+")
.replace(/_/g, "/");
const rawData = atob(base64);
const outputArray = new Uint8Array(rawData.length);
for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i);
}
return outputArray;
};
const saveSubscription = async subscription => {
console.log("Save Sub")
const SERVER_URL = "http://localhost:4000/save-subscription";
const response = await fetch(SERVER_URL, {
method: "post",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(subscription)
});
return response.json();
};
self.addEventListener("activate", async () => {
try {
const applicationServerKey = urlB64ToUint8Array(
"BFPtpIVOcn2y25il322-bHQIqXXm-OACBtFLdo0EnzGfs-jIGXgAzjY6vNapPb4MM1Z1WuTBUo0wcIpQznLhVGM"
);
const options = { applicationServerKey, userVisibleOnly: true };
const subscription = await self.registration.pushManager.subscribe(options);
console.log(JSON.stringify(subscription))
const response = await saveSubscription(subscription);
} catch (err) {
console.log(err.code)
console.log(err.message)
console.log(err.name)
console.log('Error', err)
}
});
self.addEventListener("push", function(event) {
if (event.data) {
console.log("Push event!! ", event.data.text());
} else {
console.log("Push event but no data");
}
});
Also i have created a bit of backend as well
const express = require("express");
const cors = require("cors");
const bodyParser = require("body-parser");
const webpush = require('web-push')
const app = express();
app.use(cors());
app.use(bodyParser.json());
const port = 4000;
app.get("/", (req, res) => res.send("Hello World!"));
const dummyDb = { subscription: null }; //dummy in memory store
const saveToDatabase = async subscription => {
// Since this is a demo app, I am going to save this in a dummy in memory store. Do not do this in your apps.
// Here you should be writing your db logic to save it.
dummyDb.subscription = subscription;
};
// The new /save-subscription endpoint
app.post("/save-subscription", async (req, res) => {
const subscription = req.body;
await saveToDatabase(subscription); //Method to save the subscription to Database
res.json({ message: "success" });
});
const vapidKeys = {
publicKey:
'BFPtpIVOcn2y25il322-bHQIqXXm-OACBtFLdo0EnzGfs-jIGXgAzjY6vNapPb4MM1Z1WuTBUo0wcIpQznLhVGM',
privateKey: 'mHSKS-uwqAiaiOgt4NMbzYUb7bseXydmKObi4v4bN6U',
}
webpush.setVapidDetails(
'mailto:janakprajapati90#email.com',
vapidKeys.publicKey,
vapidKeys.privateKey
)
const sendNotification = (subscription, dataToSend='') => {
webpush.sendNotification(subscription, dataToSend)
}
app.get('/send-notification', (req, res) => {
const subscription = {endpoint:"https://fcm.googleapis.com/fcm/send/dLjyDYvI8yo:APA91bErM4sn_wRIW6xCievhRZeJcIxTiH4r_oa58JG9PHUaHwX7hQlhMqp32xEKUrMFJpBTi14DeOlECrTsYduvHTTnb8lHVUv3DkS1FOT41hMK6zwMvlRvgWU_QDDS_GBYIMRbzjhg",expirationTime:null,keys:{"p256dh":"BE6kUQ4WTx6v8H-wtChgKAxh3hTiZhpfi4DqACBgNRoJHt44XymOWFkQTvRPnS_S9kmcOoDSgOVD4Wo8qDQzsS0",auth:"CfO4rOsisyA6axdxeFgI_g"}} //get subscription from your databse here.
const message = 'Hello World'
sendNotification(subscription, message)
res.json({ message: 'message sent' })
})
app.listen(port, () => console.log(`Example app listening on port ${port}!`));
Please help me
Try the following code:
index.js
const check = () => {
if (!("serviceWorker" in navigator)) {
throw new Error("No Service Worker support!");
} else {
console.log("service worker supported")
}
if (!("PushManager" in window)) {
throw new Error("No Push API Support!");
} else {
console.log("PushManager worker supported")
}
};
const saveSubscription = async subscription => {
console.log("Save Sub")
const SERVER_URL = "http://localhost:4000/save-subscription";
const response = await fetch(SERVER_URL, {
method: "post",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(subscription)
});
return response.json();
};
const urlB64ToUint8Array = base64String => {
const padding = "=".repeat((4 - (base64String.length % 4)) % 4);
const base64 = (base64String + padding)
.replace(/\-/g, "+")
.replace(/_/g, "/");
const rawData = atob(base64);
const outputArray = new Uint8Array(rawData.length);
for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i);
}
return outputArray;
};
const registerServiceWorker = async () => {
return navigator.serviceWorker.register("service.js?"+Math.random()).then((swRegistration) => {
console.log(swRegistration);
return swRegistration;
});
};
const requestNotificationPermission = async (swRegistration) => {
return window.Notification.requestPermission().then(() => {
const applicationServerKey = urlB64ToUint8Array(
"BFPtpIVOcn2y25il322-bHQIqXXm-OACBtFLdo0EnzGfs-jIGXgAzjY6vNapPb4MM1Z1WuTBUo0wcIpQznLhVGM"
);
const options = { applicationServerKey, userVisibleOnly: true };
return swRegistration.pushManager.subscribe(options).then((pushSubscription) => {
console.log(pushSubscription);
return pushSubscription;
});
});
};
const main = async () => {
check();
const swRegistration = await registerServiceWorker();
const subscription = await requestNotificationPermission(swRegistration);
// saveSubscription(subscription);
};
service.js
self.addEventListener("push", function(event) {
if (event.data) {
console.log("Push event!! ", event.data.text());
} else {
console.log("Push event but no data");
}
});
I can think of three reasons that the permission is denied
1) your site is not on https (including localhost that is not on https), the default behaviour from chrome as far as i know is to block notifications on http sites. If that's the case, click on the info icon near the url, then click on site settings, then change notifications to ask
2) if you are on Safari, then safari is using the deprecated interface of the Request permission, that is to say the value is not returned through the promise but through a callback so instead of
Notification.requestPermission().then(res => console.log(res))
it is
Notification.requestPermission(res => console.log(res))
3) Your browser settings are blocking the notifications request globally, to ensure that this is not your problem run the following code in the console (on a secured https site)
Notification.requestPermission().then(res => console.log(res))
if you receive the alert box then the problem is something else, if you don't then make sure that the browser is not blocking notifications requests

Can't figure out why my app.get is being run twice?

I have a app.get which inside of it is quite a bit of logic. Which everything works great aside from some of the logic being called twice for some reason. I have noticed when I was saving something to by db that it would save two rows.
So I put a console.log in that area and sure enough it was logging it twice.
Any reason why this is happening?
app.get('/shopify/callback', (req, res) => {
const { shop, hmac, code, state } = req.query;
const stateCookie = cookie.parse(req.headers.cookie).state;
if (state !== stateCookie) {
return res.status(403).send('Request origin cannot be verified');
}
if (shop && hmac && code) {
// DONE: Validate request is from Shopify
const map = Object.assign({}, req.query);
delete map['signature'];
delete map['hmac'];
const message = querystring.stringify(map);
const providedHmac = Buffer.from(hmac, 'utf-8');
const generatedHash = Buffer.from(
crypto
.createHmac('sha256', config.oauth.client_secret)
.update(message)
.digest('hex'),
'utf-8'
);
let hashEquals = false;
try {
hashEquals = crypto.timingSafeEqual(generatedHash, providedHmac)
} catch (e) {
hashEquals = false;
};
if (!hashEquals) {
return res.status(400).send('HMAC validation failed');
}
// DONE: Exchange temporary code for a permanent access token
const accessTokenRequestUrl = 'https://' + shop + '/admin/oauth/access_token';
const accessTokenPayload = {
client_id: config.oauth.api_key,
client_secret: config.oauth.client_secret,
code,
};
request.post(accessTokenRequestUrl, { json: accessTokenPayload })
.then((accessTokenResponse) => {
const accessToken = accessTokenResponse.access_token;
// DONE: Use access token to make API call to 'shop' endpoint
const shopRequestUrl = 'https://' + shop + '/admin/shop.json';
const shopRequestHeaders = {
'X-Shopify-Access-Token': accessToken,
}
request.get(shopRequestUrl, { headers: shopRequestHeaders })
.then((shopResponse) => {
const response = JSON.parse(shopResponse);
const shopData = response.shop;
console.log('BEING CALLED TWICE...')
res.render('pages/brand_signup',{
shop: shopData.name
})
})
.catch((error) => {
res.status(error.statusCode).send(error.error.error_description);
});
})
.catch((error) => {
res.status(error.statusCode).send(error.error.error_description);
});
} else {
res.status(400).send('Required parameters missing');
}
});

How to make a function that can be called in other functions in Cloud Functions?

I have the following case, when deleting any data, I need to delete the app badges (at the moment I delete them using silent push notication and reduce the app badges number with the cloud function) if the user who sent the request has deleted. But since the user who deleted could send several requests to different users in different places, so I decided that I need to create a function that will be called in firebase database trigger functions and also it will help not to duplicate the same code everywhere .
The function will be approximate such
function adminRemoveAppBadge(userID, dataID, categoryID) {
};
And for example, call it in this function
module.exports = functions.database.ref('/cards/{cardID}/interestedUsers/{interestedUserID}').onWrite(event => {
const currentData = event.data.current;
const prevData = event.data.previous;
const cardID = event.params.cardID;
const interestedUserID = event.params.interestedUserID;
if (currentData.val() && !prevData.val()) {
// value created
return console.log('cardInterestedUserHandler - created');
} else if (!currentData.val() && prevData.val()) {
// value removed
console.log('cardInterestedUserHandler - removed', currentData.val());
const cardRef = admin.database().ref("cards").child(cardID);
const cardRefPromise = cardRef.once("value", function(snap, error) {
if (error) {
return error;
};
if (snap.val()) {
const cardJSON = snap.val();
const cardOwnerID = cardJSON["ownerID"];
if (cardOwnerID) {
const cardOwnerAppBadgesRef = admin.database().ref("userAppBadges").child(cardOwnerID).child("appBadgeModels").orderByChild("dataID").equalTo(cardID);
const cardOwnerAppBadgesRefPromise = cardOwnerAppBadgesRef.once("value", function (cardOwnerAppBadgesRefSnap, error) {
if (error) {
return error;
};
if (cardOwnerAppBadgesRefSnap.val()) {
var deletingPromises = [];
cardOwnerAppBadgesRefSnap.forEach(function(cardOwnerAppBadgesRefSnapChild) {
const appBadgeModelJSON = cardOwnerAppBadgesRefSnapChild.val();
const appBadgeModelID = appBadgeModelJSON["id"];
const senderID = appBadgeModelJSON["senderID"];
if (appBadgeModelID && senderID) {
if (senderID == interestedUserID) {
const cardOwnerAppBadgeRef = admin.database().ref("userAppBadges").child(cardOwnerID).child("appBadgeModels").child(cardOwnerAppBadgeModelID);
const cardOwnerAppBadgeRefPromise = cardOwnerAppBadgeRef.remove();
deletingPromises.push(cardOwnerAppBadgeRefPromise);
// to call
adminRemoveAppBadge
};
} else {
console.log("cardOwnerAppBadgeModelID == null");
};
});
return Promise.all(deletingPromises);
};
});
return Promise.all([cardOwnerAppBadgesRefPromise]);
} else {
return console.log("owner id == null");
};
};
});
return Promise.all([cardRefPromise]);
} else {
return console.log('cardInterestedUserHandler - updated');
};
});
Also functions are in different files. How can I call it in other firebase cloud functions and how do I deploy this function?
Update I tried to do so one of the options as written here and here, but when I tried to do deploy I got an error Cannot find module 'AppBadges/adminRemoveAppBadge.js'.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
exports.adminRemoveAppBadge = function (userID, dataID, categoryID) {
console.log("adminRemoveAppBadge nil");
};
Requested this function so
var adminRemoveAppBadgeModule = require("AppBadges/adminRemoveAppBadge.js");
and call this functions so
adminRemoveAppBadgeModule.adminRemoveAppBadge(cardOwnerID, cardID, 0);
Google Functions are just JS - so normal routes to include code work.
I place my "library" functions in a folder /lib
So my functions folder looks like this:
/functions
/lib
BuildImage.js
SendEmail.js
index.js
package.json
...etc...
within my index.js I just include my code:
const SendMail = require('./lib/SendMail')
const sendMail = new SendMail({
database: db,
mailgun: mailgun
})
exports.sendContactUsMessage = functions.database.ref('/contact-messages/{itemId}').onWrite(sendMail.send(event))
EDIT Added /lib/SendMail.js code:
module.exports = class SendMail {
constructor(config) {
if (!config) {
throw new Error ('config is empty. Must pass database and mailgun settings')
}
if (!config.database) {
throw new Error('config.database is empty. Must pass config.database')
}
if (!config.mailgun) {
throw 'config.mailgun is empty. Must pass config.mailgun'
}
this.database = config.database
this.mailgun = config.mailgun
}
sendEmail (emailData) {
return new Promise((resolve, reject) => {
this.mailgun.messages().send(emailData, (error, body) => {
if (error) {
if (debug) {
console.log(error)
}
reject(error)
} else {
if (debug) {
console.log(body)
}
resolve(body)
}
})
})
}
...
}

Categories