FCM onMessage not trigerring in React but app server sends multicast_messages properly - python

I am sending the message from a Python script as follows.
import firebase_admin
from firebase_admin import credentials, messaging
# initializations
cred = credentials.Certificate('Full path to firebase-admin-sdk.json')
print('Connecting...')
firebase_admin.initialize_app(cred)
registration_tokens = [
'valid_registration_token_from_client',
]
message = messaging.MulticastMessage(
data={'score': '850', 'time': '2:45'},
tokens = registration_tokens
)
response = messaging.send_multicast(message)
print('{0} messages were sent successfully.'.format(response.success_count))
After executing the above code, it prints 1 message sent successfully. I looked at my Firebase console and found that the number of sent notifications increased by 1. However, my React Js client does not seem to receive the message.
In react app,
Root public folder has firebase-messaging-sw.js file,
import { initializeApp } from "firebase/app";
import { getMessaging } from "firebase/messaging/sw";
import { onBackgroundMessage } from "firebase/messaging/sw";
const firebaseConfig = {
apiKey: "MY_API_KEY",
authDomain: "FIREBASE_APP_DOMAIN",
databaseURL: "DB_URL",
projectId: "PROJECT_ID",
storageBucket: "STORAGE_BUCKET",
messagingSenderId: "SENDER_ID",
appId: "APP_ID",
measurementId: "MEASUREMENT_ID"
};
const firebaseApp = initializeApp(firebaseConfig);
const messaging = getMessaging(firebaseApp);
onBackgroundMessage(messaging, (payload) => {
console.log('[firebase-messaging-sw.js] Received background message ', payload);
// Customize notification here
const notificationTitle = 'Background Message Title';
const notificationOptions = {
body: 'Background Message body.',
icon: '/firebase-logo.png'
};
self.registration.showNotification(notificationTitle,
notificationOptions);
});
In App.js,
import React, {Component} from "react";
import { getMessaging, getToken, onMessage } from "firebase/messaging";
import { initializeApp } from "firebase/app";
export default class App extends Component {
constructor(props) {
super(props);
this.connectButtonPressed = this.connectButtonPressed.bind(this);
}
render() {
return (
<div><button onClick={this.connectButtonPressed}>Click</button></div>
)
}
connectButtonPressed(e) {
Notification.requestPermission().then((permission) => {
if (permission === 'granted') {
console.log('Notification permission granted.');
// TODO(developer): Retrieve a registration token for use with FCM.
// Get registration token. Initially this makes a network call, once retrieved
// subsequent calls to getToken will return from cache.
const firebaseConfig = {
apiKey: "API_KEY",
authDomain: "AUTH_DOMAIN",
databaseURL: "DB_URL",
projectId: "PROJECT_ID",
storageBucket: "STORAGE_BUCKET",
messagingSenderId: "SENDER_ID",
appId: "APP_ID",
measurementId: "MEASUREMENT_ID"
};
const firebaseApp = initializeApp(firebaseConfig);
const messaging = getMessaging(firebaseApp);
onMessage(messaging, (payload) => {
console.log('Message received. ', payload);
});
getToken(messaging, { vapidKey: 'VAPID_KEY_FROM_CONSOLE' }).then((currentToken) => {
if (currentToken) {
// Send the token to your server and update the UI if necessary
console.log('currentToken: ', currentToken);
} else {
// Show permission request UI
console.log('No registration token available. Request permission to generate one.');
// ...
}
}).catch((err) => {
console.log('An error occurred while retrieving token. ', err);
// ...
});
} else {
console.log('Unable to get permission to notify.');
}
});
}
}
The client can successfully request for and receive the registration token to which I send the message from the Python script. However, the onMessage event is not being triggered which makes me think that the client might not be receiving the message even though I sent the message to the token associated with this client.
What might be the issue here? What am I missing?

Related

400 error when posting PushSubscription object to server

I am setting up push notifications using service workers + websockets using Django Channels and Django WebPush to receive the subscription and send a message back to the service worker.
While I am able to register a service worker and receive the PushSubscription object containing details of a push subscription. I then try to send this subscription to an end point in webpush 'save_information' that should have the subscription. However, when I attempt to do this I get the error:
Response {type: 'cors', url: 'http://127.0.0.1:8000/webpush/save_information', redirected: false, status: 400, ok: false, …}body: (...)bodyUsed: falseheaders: Headers {}ok: falseredirected: falsestatus: 400statusText: "Bad Request"type: "cors"url: "http://127.0.0.1:8000/webpush/save_information"[[Prototype]]: Response
This is the code I have in my service worker/ the code related to getting a subscription and sending through the subscription:
const saveSubscription = async (subscription) => {
const browser = navigator.userAgent.match(/(firefox|msie|chrome|safari|trident)/ig)[0].toLowerCase();
const data = {
status_type: 'subscribe',
subscription: subscription.toJSON(),
browser: browser,
};
const res = await fetch('http://127.0.0.1:8000/webpush/save_information', {
method: 'POST',
body: JSON.stringify(data),
headers: {
'content-type': 'application/json'
},
credentials: 'include'
});
handleResponse(res);
}
const handleResponse = (res) => {
console.log(res);
}
...
self.addEventListener('activate', async () => {
try {
const applicationServerKey = urlB64ToUint8Array('***')
const options = { applicationServerKey,
userVisibleOnly: true }
const subscription = await self.registration.pushManager.subscribe(options)
saveSubscription(subscription)
} catch (err) {
console.log('Error', err)
}
})
and in my server:
path('webpush/', include('webpush.urls')),
Would appreciate any help

Flask-Login doesn't set cookies to browser with angular?

I'm building a fullstack web project using Angular 6 & Python Flask, mainly using Flask-Security extension.
Currently, I try to implement a login system of users, using the login_user() (Flask-Login's method). Basiclly, login_user() works, but I can't see any session cookies on my browser.
As documentation says, every change/new instance of session object sets/modifies cookies accordingly, so login_user() creates a new instance of session.
I'm running and testing the project on 'http://127.0.0.1:4200' (Angular default port), and Flask, using 'http://127.0.0.1:5000/'.
As a last resort, I tried to build a Flask app without any actual frontend, running and testing it from 'http://127.0.0.1:5000/', and it did work. I've managed to see the cookies that login_user() should've set from the start.
Mainly, my question, why doesn't it work with Angular?
frontend code:
export class Login {
constructor(private userSerivce : UserService, private router : Router) {}
outputMessage : string = null;
loginOnSubmit(form : FormGroup): void {
let formDict = {
"email" : form.controls["email"].value,
"password" : form.controls["password"].value
}
this.userSerivce.loginOnSubmit(formDict).subscribe({
next : value => {
//whatever, still didn't get here
},
error : response => {this.outputMessage = response.error}
})
}
backend login function:
#user_app.route('/signin', methods=['POST'])
def signIn():
session.permanent = True
status_code = 200
output_string = None
form = json.loads(request.data.decode('utf-8'))
user = User.query.filter_by(email=form['email']).first()
if user is not None:
if utils.verify_password(form['password'],user.password) and user.is_authenticated:
user.active = True
db.session.commit()
if login_user(user, True, datetime.timedelta(days=24), False, True):
i=1 #debugging purposes only
else:
status_code = 400
output_string = "error"
else:
status_code = 400
output_string = "error"
return jsonify(1), status_code
The models is exactly as documentation suggests, I even used the same code in my empty Flask app (the same classes and database, and as I said, it worked).
You can't set browser cookies by using the server session. You'd have to send cookies in the response. If you want to set cookies in the response, you could do something like this:
from flask import make_response # and your other stuff
# ... other imports ...
def login():
# ... some authentication code here to get your access_token (like a jwt)...
resp = make_response(redirect('http://localhost:4200')) # your angular app
resp.set_cookie('token', access_token) # set the cookie on the response header for the browser to extract
return resp # return the response with the new cookie attached
Since your client application isn't on the same domain as your server application, setting the session isn't going to help you in the way you want for authentication. The best way to do what you want is to pass a JWT back and forth between client and server.
One thing you can try to do (if you want to set some kind of authentication on the front end) would be to authenticate your user return a JWT back to Angular. You could then set an http header to come to the backend each time. The backend would parse the request and extract the JWT from the header. You would then use that header to authenticate the user's request to your backend by decrypting the JWT when it comes in. There is a great deal of literature on this. I'll put in some good tutorials at the end of this post.
You can use (in Angular) an HttpInterceptor. Something like this:
import { Injectable } from "#angular/core";
import { HttpInterceptor, HttpHandler, HttpEvent } from "#angular/common/http";
import { AuthService } from "../auth/auth.service";
import { HttpRequest } from '#angular/common/http';
import { Observable } from "rxjs";
#Injectable()
export class TokenInterceptor implements HttpInterceptor {
constructor(public auth: AuthService) { }
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (this.auth.isLoggedIn()) {
request = request.clone({
setHeaders: {
Authorization: `Bearer ${this.auth.getToken()}`
}
});
}
return next.handle(request);
}
}
You could have an Auth service like so:
import { Injectable } from '#angular/core';
import { HttpClient, HttpParams, HttpHeaders } from '#angular/common/http';
import { CookieService } from 'ngx-cookie-service';
import { map } from 'rxjs/operators';
import { environment } from '../../environments/environment';
#Injectable({
providedIn: 'root'
})
export class AuthService {
redirectUrl: string;
// cookie service from ngx-cookie-service
constructor(private http: HttpClient, private cookieService: CookieService) { }
checkToken() {
return this.cookieService.check('token');
}
getToken() {
return this.cookieService.get('token');
}
loginWithUsernameAndPassword(userName: string, password: string) {
return this.http.post<any>(`${environment.API_URL}/auth/login`,
new HttpParams({fromObject: {userName, password}}),
{
headers: new HttpHeaders().set('Content-Type', 'application/x-www-form-urlencoded')
}
).pipe(map(user => {
if (user && user.token) {
this.cookieService.set('token', user.token);
}
return user;
}));
}
logout() {
this.cookieService.delete('token');
}
isLoggedIn() {
return this.cookieService.check('token');
}
registerWithUsernameAndPassword(userName, password, email) {
return this.http.post<any>(`${environment.API_URL}/auth/create`,
new HttpParams({fromObject: {userName, password, email}}),
{
headers: new HttpHeaders().set('Content-Type', 'application/x-www-form-urlencoded')
}
)
.pipe(map(user => {
console.log(user);
return user;
}));
}
}
In your AppModule you can then specify a provider called HTTP_INTERCEPTORS and use the HttpInterceptor you created -- in my case, I would call it TokenInterceptor:
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing/app-routing.module';
import { SharedModule } from './shared/shared.module';
import { HttpClientModule, HTTP_INTERCEPTORS } from '#angular/common/http';
import { CookieService } from 'ngx-cookie-service';
import { AuthService } from './auth/auth.service';
import { TokenInterceptor } from './interceptors/token.interceptor';
#NgModule({
imports: [
BrowserModule,
AppRoutingModule,
SharedModule,
HttpClientModule
],
declarations: [
AppComponent,
],
exports: [],
providers: [
AuthService,
CookieService,
{
provide: HTTP_INTERCEPTORS,
useClass: TokenInterceptor,
multi: true
}
],
bootstrap: [AppComponent]
})
export class AppModule {
}
A good reference for the interceptor: https://angular.io/api/common/http/HttpInterceptor
and: https://medium.com/#ryanchenkie_40935/angular-authentication-using-the-http-client-and-http-interceptors-2f9d1540eb8
And the canonical source on Flask would be Miguel Grinberg, who has written some JWT authentication tutorials -- https://blog.miguelgrinberg.com/post/json-web-tokens-with-public-key-signatures
here's another tutorial for JWT in Flask as well: https://realpython.com/token-based-authentication-with-flask/

Service Worker does not fire upon fetch and return cached data

I am using service workers in one of my projects.
On call, my service worker gets successfully registered and it caches all provided URLS. But when I reload my page, all cached data is either picked from disk cache or memory cache and not from service worker.
I refered this question Service worker is caching files but fetch event is never fired and cross checked that there is not a problem with scoping.
service-worker.js
var CACHE_NAME = 'home-screen-cache';
var urlsToCache = [
'/static/client-view/fonts/fontawesome-webfont.eot',
'/static/client-view/fonts/fontawesome-webfont.woff',
'/static/client-view/fonts/fontawesome-webfont.ttf',
'/static/client-view/fonts/fontawesome-webfont.svg',
'/static/css/cricket.css',
'/static/client-view/js/jquery.mobile.custom.min.js',
'/static/client-view/js/jquery-2.1.4.min.js',
'/static/js/cricket_matches.js'
];
self.addEventListener('install', function(event) {
// Perform install steps
event.waitUntil(
caches.open(CACHE_NAME)
.then(function(cache) {
console.log('Opened cache');
return cache.addAll(urlsToCache);
})
);
});
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.match(event.request)
.then(function(response) {
// Cache hit - return response
if (response) {
console.log("Found in cache");
return response;
}
console.log("Not found in cache");
return fetch(event.request);
}
)
);
});
service-worker registration script
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('{% static 'service-worker.js' %}').then(function(registration) {
// Registration was successful
console.log('ServiceWorker registration successful with scope: ', registration.scope);
}, function(err) {
// registration failed :(
console.log('ServiceWorker registration failed: ', err);
});
});
}
If you need any kind of additional data, mention in comments.
Please help.

sending rpc_client message from a react project

how can i send a RPC message (based on rabbitmq)from a reactJs project in frontend to a python project in backend .
i tried to use amqplib to send my message but i think i can't use it in a browser .
any help !
this is my code :
export function callService() {
var amqp = require('amqplib/callback_api');
amqp.connect('amqp://localhost:8000', function (err, conn) {
console.log("gggggggg")
conn.createChannel(function(err, ch) {
var q = 'frontend';
var msg = 'Hello!';
let messsage_body="http://www.documents.sergic.com/photos/photo-location-appartement-1-piece-pontoise-95300_563687_1_101_S.JPG";
var headers={'type': 'get_similarity', 'customer': 'sergic'};
console.log('cc ', num);
ch.consume(q.queue, function(msg) {
if (msg.properties.correlationId === corr) {
console.log(' [.] Got %s', msg.content.toString());
setTimeout(function() { conn.close(); process.exit(0) }, 500);
}
}, {noAck: true});
});
}
and i want to run it in a browser by clicking on a picture !

Is it OK to pass both token and client_id to the client when Channel API is used?

I need to create an application, where GAE server will always talk with just one client (i.e. one message should be always sent just to one client).
I do the following -
Python:
def get(self):
# generate token, when page is loaded
client_id = uuid.uuid4().hex
token = channel.create_channel(client_id)
template_values = {'token': token,
'client_id': client_id
}
self.response.out.write(template.render('page.html', template_values))
def post(self):
# reply to the client
...
client_id = self.request.get('id')
channel.send_message(client_id, message)
Javascript:
sendMessage = function(field) {
$.ajax({
type: "POST",
url: "/",
data: "f=" + field + "&id=" + "{{ client_id }}", // WARNING!
contentType: "application/x-www-form-urlencoded",
success: function(data) {
}
});
};
onOpened = function() {
connected = true;
sendMessage('opened');
};
onMessage = function(msg) {
alert(msg.data);
};
onError = function(err) {
alert(err);
};
onClose = function() {
alert("close");
};
// open new session
channel = new goog.appengine.Channel('{{ token }}'); // WARNING!
socket = channel.open();
socket.onopen = onOpened;
socket.onmessage = onMessage;
socket.onerror = onError;
socket.onclose = onClose;
It works well, but with such scenario both token and client_id are passed to the client. Is it OK?
There's no technical reason not to do this. If you're worried about security, the token is far more valuable: an attacker who could listen to your traffic could take the token and listen to channel messages in a different context. The clientid wouldn't let them do that.
But I do have a question: why not return the message in the POST response, rather than sending a message over the channel? Or is the sample code just simplified for the example?

Categories