How to avoid sending notifications to 'Miscellaneous' channel at Firebase? - javascript

I want to show my notifications in a channel that I create so I can fully customize my channel with my preferences. I'm using a Firebase function to send notifications (messages) from user to user:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.pushNotification = functions.firestore.document('/devices/{tokenId}/notifications/{notificationId}')
.onWrite((change, context) => {
console.log('Push notification event triggered');
const tokenId = context.params.tokenId;
const document = change.after.exists ? change.after.data() : null;
if (document == null) {
return console.log('A notification has been deleted from database');
}
const payload = {
notification: {
title: document.username,
body: document.message,
sound: "default"
},
data: {
sender: document.sender
}
};
const options = {
priority: "high",
timeToLive: 60 * 60 * 24 //24 hours
};
return admin.messaging().sendToDevice(tokenId, payload, options).then(result => {
console.log('A notification sent to device with tokenId: ', tokenId);
});
});
I have implemented my FirebaseMessagingService service:
#Override
public void onMessageReceived(#NonNull RemoteMessage remoteMessage) {
showNotification(remoteMessage);
}
private void showNotification(RemoteMessage remoteMessage) {
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
RemoteMessage.Notification remoteNotification = remoteMessage.getNotification();
if (remoteNotification == null) return;
String title = remoteNotification.getTitle();
String message = remoteNotification.getBody();
Notification notification;
Notification.Builder builder = android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O ?
new Notification.Builder(this, CH_MESSAGE) : new Notification.Builder(this);
notification = builder
.setContentTitle(title)
.setContentText(message)
.setCategory(CATEGORY_MESSAGE)
.build();
notificationManager.notify(0, notification);
}
And created my own notification channel at my Application class:
#Override
public void onCreate() {
super.onCreate();
createNotificationChannels();
}
private void createNotificationChannels() {
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
if (notificationManager == null) return;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel notificationChannel = new NotificationChannel(CH_MESSAGE,
getString(R.string.messages), NotificationManager.IMPORTANCE_HIGH);
notificationChannel.setDescription(getString(R.string.message_channel_description));
notificationManager.createNotificationChannel(notificationChannel);
}
}
I can successfully send notifications, but notifications are going to Miscellaneous channel.
I tried to remove the channel by using its channel ID with notificationManager.deleteNotificationChannel("fcm_fallback_notification_channel");, but it still recreates the channel and sends the notification there. How can I remove Miscellaneous channel permanently and handle my notifications with my own channels?

I have found the problem I am facing, my message payload contains both notification and data fields. According to this documentation my messages were not calling onMessageReceived method when app is in background. Now I'm using only data payload which calls the method when app is at both background and foreground.
const payload = {
data: {
sender: document.sender,
username: document.username,
message: document.message
}
};
And in my onMessageReceived method:
Map<String, String> remoteMap = remoteMessage.getData();
String senderUid = remoteMap.get("sender");
String senderUsername = remoteMap.get("username");
String message = remoteMap.get("message");
Notification.Builder builder = android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O ?
new Notification.Builder(this, CH_MESSAGE) : new Notification.Builder(this);
Notification notification = builder
.setContentTitle(senderUsername)
.setContentText(message)
.setSmallIcon(R.drawable.ic_notif_message)
.build();
notificationManager.notify(0, notification);

Related

Broadcasting to everyone except sender socket.io

I am building a chat in my app. I am using socket.io for this. When a user sends a message I send an api request to my server. The api stores the message in the database and only then emits, with a socket service to everyone in the room that there is a new message. I have a SocketService class with this method:
private async broadcast({ type, data, chatId, senderId }: { type: string; data: any; chatId: string; senderId: string }) {
const excludedSocket = await this.getUserSocket(senderId);
if (chatId && excludedSocket) {
excludedSocket.emit;
} else if (excludedSocket) {
excludedSocket.emit(type, data);
} else if (room) {
gIo.to(room).emit(type, data);
} else {
gIo.emit(type, data);
}
}
The problem I have is that getUserSocket returns a RemoteSocket object that doesn't have the broadcast or methods on it. So how can I achieve this?
private async getUserSocket(userId: string) {
const sockets = await this.getAllSockets();
const socket = sockets.find((s) => s.data.uid === userId);
return socket;
}
private async getAllSockets() {
const sockets = await this.io.fetchSockets();
return sockets
}
socket.broadcast.emit equates to a BroadcastOperator that sends to an empty Set of rooms (meaning "all rooms"), excluding the socket id "room" of the sender Socket.
The same BroadcastOperator can be created from the server instance with server.except()
const sockets = await this.io.except(socketIdString).emit('blah', x)

JWT token stored in localstorage lost after window.location.href call

I am using Javascript Fetch API to invoke Dot net web API.
Using Visual Studio code for HTML/Javascript & Visual Studio 2019 for Dot Net Web API.
I am trying to implement login functionality using Dot net C# Web API, that will return JWT token in the response and use the token later to invoke separate Web API/Service (e.g. EmployeeInfo ).
Login page :
It displays user id and password fields and the button "Login"
Once user clicks on login button, the function fnlogin is invoked
function fnlogin() {
const uname = document.getElementById('uname').value;
const pwd = document.getElementById('pwd').value;
const logindata = {
username: uname,
password: pwd
}
const loginurl = 'http://localhost:13402/api/Auth/Login';
authenticate(loginurl, logindata);
}
async function authenticate(loginurl, logindata) {
console.log(logindata)
const response = await fetch(loginurl , {
method: "POST",
mode: "cors",
body: JSON.stringify(logindata),
headers: { "Content-type" : "application/json, charset=UTF-8"}
});
const rdata = await response.json();
console.log(rdata);
if (!rdata.success) {
document.getElementById("loginMessage").innerHTML = rdata.message;
return;
}
const inMemoryToken = rdata.data
localStorage.setItem('user', JSON.stringify(rdata));
window.location.href = "http://localhost:5500/Employeeinfo1.html";
}
The Web API returns JWT token properly.
The code in controller is as follows :
[HttpPost("Login")]
public async Task<ActionResult<ServiceResponse<string>>> Login(UserLoginDto request)
{
var response = await _authRepo.Login(
request.Username, request.Password
);
if (!response.Success)
{
return BadRequest(response);
}
return Ok(response);
}
``
The code in AuthRepository class is as follows :
public class AuthRepository : IAuthRepository
{
private readonly AppDbContext _context;
private readonly IConfiguration _configuration;
public AuthRepository(AppDbContext context, IConfiguration configuration)
{
_configuration = configuration;
_context = context;
}
public async Task<ServiceResponse<string>> Login(string username, string password)
{
var response = new ServiceResponse<string>();
var user = await _context.Users.FirstOrDefaultAsync(x => x.Username.ToLower().Equals(username.ToLower()));
if (user == null)
{
response.Success = false;
response.Message = "User not found.";
}
else if (!VerifyPasswordHash(password, user.PasswordHash, user.PasswordSalt))
{
response.Success = false;
response.Message = "Wrong password.";
}
else
{
response.Data = CreateToken(user);
}
return response;
}
public async Task<ServiceResponse<User>> Register(User user, string password)
{
ServiceResponse<User> response = new ServiceResponse<User>();
if (await UserExists(user.Username))
{
response.Success = false;
response.Message = "User already exists.";
return response;
}
CreatePasswordHash(password, out byte[] passwordHash, out byte[] passwordSalt);
user.PasswordHash = passwordHash;
user.PasswordSalt = passwordSalt;
_context.Users.Add(user);
await _context.SaveChangesAsync();
response.Data = user;
return response;
}
public async Task<bool> UserExists(string username)
{
if (await _context.Users.AnyAsync(x => x.Username.ToLower().Equals(username.ToLower())))
{
return true;
}
return false;
}
private void CreatePasswordHash(string password, out byte[] passwordHash, out byte[] passwordSalt)
{
using (var hmac = new System.Security.Cryptography.HMACSHA512())
{
passwordSalt = hmac.Key;
passwordHash = hmac.ComputeHash(System.Text.Encoding.UTF8.GetBytes(password));
}
}
private bool VerifyPasswordHash(string password, byte[] passwordHash, byte[] passwordSalt)
{
using (var hmac = new System.Security.Cryptography.HMACSHA512(passwordSalt))
{
var computedHash = hmac.ComputeHash(System.Text.Encoding.UTF8.GetBytes(password));
for (int i = 0; i < computedHash.Length; i++)
{
if (computedHash[i] != passwordHash[i])
{
return false;
}
}
return true;
}
}
private string CreateToken(User user)
{
var claims = new List<Claim>
{
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
new Claim(ClaimTypes.Name, user.Username)
};
var key = new SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes(_configuration.GetSection("AppSettings:Token").Value));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha512Signature);
var tokendDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(claims),
Expires = System.DateTime.Now.AddDays(1),
SigningCredentials = creds
};
var tokenHandler = new JwtSecurityTokenHandler();
var token = tokenHandler.CreateToken(tokendDescriptor);
return tokenHandler.WriteToken(token);
}
4. Once the JWT token is returned , it is stored in LocalStorage. I verified in Chrome Dev Tools
But after the next screen is displayed, when I check in Chrome dev tools, the object/token is no longer present in the Local Storage.
Is this because of window.location.href will remove all local data in localStorage in the browser ?
I want to be able to use the token to pass to remaining html screens and web api
You are storing an item in the local storage of http://127.0.0.1:5500 and trying to read it from http://localhost:5500.
From Window.localStorage in MDN Web Docs:
The localStorage read-only property of the window interface allows you to access a Storage object for the Document's origin;
And about Origin:
Web content's origin is defined by the scheme (protocol), host (domain), and port of the URL used to access it. Two objects have the same origin only when the scheme, host, and port all match.
As you see, for the browser the origins are different because the host does not match: 127.0.0.1 is not the same string as localhost.
Use either localhost or 127.0.0.1 everywhere, but do not use both.

oidc-client to configure discovery documentation from the local host or other URL

Is their any way to configure discovery document from local host before the login using OIDC-Client in angular 8 application.
I have this manager which is a helper call for the OIDC client
export class AuthenticationService {
#Output() initialized: boolean = false;
static USER_LOADED_EVENT = "USER_LOADED";
static USER_UNLOADED_EVENT = "USER_UNLOADED";
//static USER_SIGNED_OUT_EVENT = "USER_SIGNED_OUT";
//static USER_EXPIRED_EVENT = "USER_EXPIRED";
static USER_RESET_EVENT = "USER_RESET";
private manager: UserManager;
private user: User = null;
private accessToken: Object = null;
private signingOut: boolean = false;
private listeners: Object;
private eventsSubject: Subject<any>;
private events: Observable<any>;
public settings: UserManagerSettings;
constructor(
private $log: Logger,
private tokenHelper: TokenHelperService,
private ngZone: NgZone, private oauthService: OAuthService) {
//Hook up some event notifications
this.listeners = {};
this.eventsSubject = new Subject<any>();
this.events = from(this.eventsSubject);
this.events.subscribe(
({ name, args }) => {
if (this.listeners[name]) {
for (let listener of this.listeners[name]) {
listener(...args);
}
}
});
}
async serviceIsReady(): Promise<void> {
await new Promise((resolve, reject) => {
const source = timer(0, 100).subscribe(t => {
if (this.initialized) {
source.unsubscribe();
resolve(true);
}
else if (t > 5000) {
source.unsubscribe();
reject(false);
}
}, error => {
reject(error);
});
});
}
/**
* Initializes the OIDC Client ready for use by the application.
*/
async initialize(openIdSettings: IOpenIdOptions): Promise<void> {
if (this.initialized) return;
this.ngZone.runOutsideAngular(() => {
this.settings = this.getClientSettings(openIdSettings);
this.manager = new UserManager(this.settings);
//Persist settings for easy access by the silent-renew iframe
window["oidc"] = {
settings: this.settings
};
});
var self = this;
this.manager.events.addAccessTokenExpiring(() => {
this.$log.info("IdSvr token expiring", new Date());
});
this.manager.events.addAccessTokenExpired(() => {
this.$log.info("IdSvr token expired", new Date());
this.logout(false);
//this.broadcast(AuthenticationService.USER_EXPIRED_EVENT);
this.broadcast(AuthenticationService.USER_RESET_EVENT);
});
this.manager.events.addSilentRenewError(e => {
this.$log.warn("IdSvr silent renew error", e.message, new Date());
this.logout(false);
});
this.manager.events.addUserLoaded(user => {
this.$log.info("IdSvr user session is ready", new Date());
this.accessToken = self.tokenHelper.getPayloadFromToken(user.access_token, false);
this.user = user;
this.broadcast(AuthenticationService.USER_LOADED_EVENT, user);
});
this.manager.events.addUserUnloaded(() => {
this.$log.info("IdSvr user session has ended", new Date());
this.broadcast(AuthenticationService.USER_UNLOADED_EVENT);
if (!this.signingOut) {
this.startAuthentication(window.location.pathname + window.location.search);
}
});
this.manager.events.addUserSignedOut(() => {
this.$log.info("IdSvr user signed out", new Date());
this.logout(false);
//this.broadcast(AuthenticationService.USER_SIGNED_OUT_EVENT);
this.broadcast(AuthenticationService.USER_RESET_EVENT);
});
this.user = await this.manager.getUser();
this.initialized = true;
}
/**
* Gets the Authorization header, to be added to any outgoing requests, that needs to be authenticated.
*/
getAuthorizationHeaders(): HttpHeaders {
return new HttpHeaders({ 'Authorization': this.getAuthorizationHeaderValue() });
}
/**
* Checks to see if a user is currently logged on.
*/
isLoggedIn(): boolean {
return this.user != null && !this.user.expired;
}
/**
* Gets all the claims assigned to the current logged on user.
*/
getProfile(): any {
return this.user.profile;
}
/**
* Gets all the claims assigned to the current logged on user.
*/
getAccessToken(): any {
return this.accessToken || this.tokenHelper.getPayloadFromToken(this.user.access_token, false);;
}
/**
* Checks to see if the current logged on user has the specified claim
* #param claimType The type of the claim the user must be assigned
* #param value The value of the claim, uses the wildcard "*", if no value provided.
*/
hasClaim(claimType: string, value?: string): boolean {
var upperValue = value === undefined || value === null
? "*"
: value.toUpperCase();
if (this.isLoggedIn()) {
const claims = this.getAccessToken()[claimType];
if (!claims)
return false;
if (typeof claims === "string")
return claims.toUpperCase() === upperValue;
else if (Object.prototype.toString.call(claims) === "[object Array]")
if (claims.filter((c) => {
return c.toUpperCase() === upperValue;
})
.length >
0)
return true;
}
return false;
}
/**
* Checks to see if the current logged on user has any of the specified claims
* #param claimTypes The type of the claim
* #param value The value of the claim, uses the wildcard "*", if no value provided.
*/
hasAnyClaim(claimTypes: string[], value?: string) {
if (this.isLoggedIn())
return false;
for (let i = 0; i < claimTypes.length; i++) {
if (this.hasClaim(claimTypes[i], value))
return true;
}
return false;
}
/**
* Gets the access token of the current logged on user.
*/
getAuthorizationHeaderValue(): string {
return `${this.user.token_type} ${this.user.access_token}`;
}
/**
* Initiates the logon process, to authenticate the user using Identity Server.
* #param returnUrl The route to load, post authentication.
*/
async startAuthentication(returnUrl: string): Promise<void> {
await this.manager.clearStaleState();
await this.manager.signinRedirect({
data: {
returnUrl: returnUrl
}
}).catch(err => {
this.$log.debug("IdSvr sign in failed", err);
return err;
});
}
/**
* Processes the callback from Identity Server, post authentication.
*/
async completeAuthentication(): Promise<Oidc.User> {
let user = await new Promise<Oidc.User>((resolve, reject) => {
this.ngZone.runOutsideAngular(() => {
this.manager.signinRedirectCallback().then(user => {
resolve(user);
}).catch(error => {
reject(error);
});
});
});
this.$log.debug("IdSvr user signed in");
this.user = user;
return user;
}
// private delay(ms: number): Promise<void> {
// return new Promise<void>(resolve =>
// setTimeout(resolve, ms));
// }
/**
* Logs out the current logged in user.
*/
logout(signoutRedirect?: boolean) {
if (signoutRedirect === undefined || signoutRedirect !== false) {
this.signingOut = true;
signoutRedirect = true;
}
this.manager.stopSilentRenew();
this.manager.removeUser().then(() => {
this.manager.clearStaleState();
this.$log.debug("user removed");
if (signoutRedirect) {
this.manager.signoutRedirect();
}
}).catch(err => {
this.$log.error(err);
});
}
/**
* Gets the current logged in user.
*/
async getUser(): Promise<Oidc.User> {
return await this.manager.getUser();
}
/**
* Gets the Identity Server settings for this client application.
*/
getClientSettings(configuration: IOpenIdOptions): UserManagerSettings {
return {
authority: configuration.authority + '/',
client_id: configuration.clientId,
redirect_uri: configuration.redirectUri,
post_logout_redirect_uri: configuration.redirectUri,
response_type: configuration.responseType, // "id_token token",
scope: "openid profile email " + configuration.apiResourceId,
filterProtocolClaims: true,
loadUserInfo: true,
automaticSilentRenew: true,
monitorSession: true,
silent_redirect_uri: configuration.silentRedirectUri,
accessTokenExpiringNotificationTime: 20, //default 60
checkSessionInterval: 5000, //default 2000
silentRequestTimeout: 20000//default: 10000
};
}
on(name, listener) {
if (!this.listeners[name]) {
this.listeners[name] = [];
}
this.listeners[name].push(listener);
}
broadcast(name, ...args) {
this.eventsSubject.next({
name,
args
});
}
}
export function authenticationServiceFactory(authService: AuthenticationService, appSettings: AppSettingsService) {
return async () => {
await appSettings.serviceIsReady();
await authService.initialize(appSettings.getOpenIdOptions());
}
};
All the configuration settings are inside the getClientSettings method.
Due to some security issue, I am not able to read the discovery document from the okta
Access to XMLHttpRequest at 'https://dev-166545.okta.com/oauth2/aus1igd7yewoAs4xa357/.well-known/openid-configuration' from origin 'https://localhost:44307' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Related problem link
Access to XMLHttpRequest at 'xxx/.well-known/openid-configuration' from origin 'xxxx' has been blocked by CORS
I am looking for a way to configure the discovery document from other location. So that CORS issue won't appear. Is there any way to configure the discovery document in the OIDC-Client library
Did some research on https://github.com/IdentityModel/oidc-client-js and haven't found the configure setting
Tried this configuration but seems not be working
getClientSettings(configuration: IOpenIdOptions): UserManagerSettings {
return {
authority: configuration.authority + '/',
client_id: configuration.clientId,
redirect_uri: configuration.redirectUri,
post_logout_redirect_uri: configuration.redirectUri,
response_type: configuration.responseType, // "id_token token",
scope: "openid profile email " + configuration.apiResourceId,
filterProtocolClaims: true,
loadUserInfo: true,
automaticSilentRenew: true,
monitorSession: true,
silent_redirect_uri: configuration.silentRedirectUri,
accessTokenExpiringNotificationTime: 20, //default 60
checkSessionInterval: 5000, //default 2000
silentRequestTimeout: 20000,//default: 10000
metadata: {
issuer: 'https://dev-166545.okta.com/oauth2/aus1igd7yewoAs4xa357',
jwks_uri: 'https://dev-166545.okta.com/oauth2/aus1igd7yewoAs4xa357/v1/keys',
end_session_endpoint: 'https://dev-166545.okta.com/oauth2/aus1igd7yewoAs4xa357/v1/logout',
authorization_endpoint: 'https://dev-166545.okta.com/oauth2/aus1igd7yewoAs4xa357/v1/authorize'
}, signingKeys: ["HS256", "HS384", "HS512", "RS256", "RS384", "RS512", "ES256", "ES384", "ES512"]
};
}
Reference
https://github.com/IdentityModel/oidc-client-js/issues/275
https://github.com/OHIF/Viewers/issues/616
Here is the discovery documentation that I get from the issuer
https://dev-166545.okta.com/oauth2/aus1igd7yewoAs4xa357/.well-known/openid-configuration
{
"issuer": "https://dev-166545.okta.com/oauth2/aus1igd7yewoAs4xa357",
"authorization_endpoint": "https://dev-166545.okta.com/oauth2/aus1igd7yewoAs4xa357/v1/authorize",
"token_endpoint": "https://dev-166545.okta.com/oauth2/aus1igd7yewoAs4xa357/v1/token",
"userinfo_endpoint": "https://dev-166545.okta.com/oauth2/aus1igd7yewoAs4xa357/v1/userinfo",
"registration_endpoint": "https://dev-166545.okta.com/oauth2/v1/clients",
"jwks_uri": "https://dev-166545.okta.com/oauth2/aus1igd7yewoAs4xa357/v1/keys",
"response_types_supported": ["code", "id_token", "code id_token", "code token", "id_token token", "code id_token token"],
"response_modes_supported": ["query", "fragment", "form_post", "okta_post_message"],
"grant_types_supported": ["authorization_code", "implicit", "refresh_token", "password"],
"subject_types_supported": ["public"],
"id_token_signing_alg_values_supported": ["RS256"],
"scopes_supported": ["monash-identity-api", "openid", "profile", "email", "address", "phone", "offline_access"],
"token_endpoint_auth_methods_supported": ["client_secret_basic", "client_secret_post", "client_secret_jwt", "private_key_jwt", "none"],
"claims_supported": ["iss", "ver", "sub", "aud", "iat", "exp", "jti", "auth_time", "amr", "idp", "nonce", "name", "nickname", "preferred_username", "given_name", "middle_name", "family_name", "email", "email_verified", "profile", "zoneinfo", "locale", "address", "phone_number", "picture", "website", "gender", "birthdate", "updated_at", "at_hash", "c_hash"],
"code_challenge_methods_supported": ["S256"],
"introspection_endpoint": "https://dev-166545.okta.com/oauth2/aus1igd7yewoAs4xa357/v1/introspect",
"introspection_endpoint_auth_methods_supported": ["client_secret_basic", "client_secret_post", "client_secret_jwt", "private_key_jwt", "none"],
"revocation_endpoint": "https://dev-166545.okta.com/oauth2/aus1igd7yewoAs4xa357/v1/revoke",
"revocation_endpoint_auth_methods_supported": ["client_secret_basic", "client_secret_post", "client_secret_jwt", "private_key_jwt", "none"],
"end_session_endpoint": "https://dev-166545.okta.com/oauth2/aus1igd7yewoAs4xa357/v1/logout",
"request_parameter_supported": true,
"request_object_signing_alg_values_supported": ["HS256", "HS384", "HS512", "RS256", "RS384", "RS512", "ES256", "ES384", "ES512"]
}
If CORS is blocked then you'll need to run the following steps.
It is not the correct solution though - you should get your bosses + IT team to instead agree to configure Okta in the standard way for an SPA.
Configure OIDC Client with no authority url
Configure OIDC Client with an explicit issuer + authorization endpoint
Configure OIDC Client to not get user info
Give OIDC Client the token signing keys as data
Use the implicit flow, with response_type=token id_token
Here is a configuration that I used back when Azure AD did not allow CORS requests from SPAs:
// OIDC Settings that work when there is no CORS support
const settings = {
// OIDC client seems to require at least a dummy value for this
authority: 'x',
// Supply these details explicitly
metadata: {
issuer: 'https://sts.windows.net/7f071fbc-8bf2-4e61-bb48-dabd8e2f5b5a/',
authorization_endpoint: 'https://login.microsoftonline.com/7f071fbc-8bf2-4e61-bb48-dabd8e2f5b5a/oauth2/authorize',
},
// When CORS is disabled, token signing keys cannot be retrieved
// The keys must be retrieved first by double hopping from the UI to API to Auth Server
signingKeys: tokenSigningKeys,
// Turn off calls to user info since CORS will block it
loadUserInfo: false,
// The URL where the Web UI receives the login result
redirect_uri: 'https://web.mycompany.com/spa/',
// The no longer recommended implicit flow must be used if CORS is disabled
response_type: 'token id_token',
// Other OAuth settings
client_id: '0ed1c9d0-68e7-4acc-abd1-a0efab2643c8',
scope: 'openid email profile',
} as UserManagerSettings;
this._userManager = new UserManager(settings);
To get the token signing keys the UI will need to double hop via your API to the JWKS endpoint. Note that JWKS keys are public information and getting them does not need securing - this is the JWKS Endpoint for my developer Azure account.

fcm notification showing three times whenever change occur in database

i am currently using firebase as database and to notify the user whenever any changes occur on database i am using fcm . i have created firebase function through node.js and whenever any change occurs on database it'triggered and notification will be send to the user but instead of showing notification once its showing three times the same notification. i search a lot on internet but have not found the solution yet.
Here is the screenShot https://imgur.com/a/zNXjo
Any help will be appreciated.
This is my FirebaseMessagingClass
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.graphics.BitmapFactory;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Build;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;
/**
* Created by HP on 04-07-2017.
*/
public class MyFirebaseMessagingService extends FirebaseMessagingService {
private static final String TAG = "FirebaseMessagingServce";
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
String notificationTitle = null, notificationBody = null;
super.onMessageReceived(remoteMessage);
if (remoteMessage.getNotification() != null) {
Log.d(TAG, "Message Notification Body: " + remoteMessage.getNotification().getBody());
notificationTitle = remoteMessage.getNotification().getTitle();
notificationBody = remoteMessage.getNotification().getBody();
}
// Also if you intend on generating your own notifications as a result of a received FCM
// message, here is where that should be initiated. See sendNotification method below.
sendNotification(notificationTitle, notificationBody);
}
private void sendNotification(String notificationTitle, String notificationBody) {
Intent intent = new Intent(this, FragmentTwo.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent,
PendingIntent.FLAG_ONE_SHOT);
Uri defaultSoundUri= RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this);
// .setAutoCancel(true)
// .setSmallIcon(R.mipmap.ic_launcher)
// .setContentIntent(pendingIntent)
// .setSound(defaultSoundUri);
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
{
notificationBuilder.setSmallIcon(R.mipmap.ic_launcher) //Notification icon
.setLargeIcon(BitmapFactory.decodeResource(this.getResources(),
R.mipmap.ic_launcher))
.setAutoCancel(true) //Automatically delete the notification
.setSound(defaultSoundUri)
.setContentTitle(notificationTitle)
.setContentText(notificationBody)
.setContentIntent(pendingIntent);
} else
{
notificationBuilder.setSmallIcon(R.mipmap.ic_launcher)
.setAutoCancel(true)
.setSound(defaultSoundUri)
.setContentTitle(notificationTitle)
.setContentText(notificationBody)
.setContentIntent(pendingIntent);
}
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(0, notificationBuilder.build());
}
}
Ans this is my service class
import android.util.Log;
import com.google.firebase.iid.FirebaseInstanceId;
import com.google.firebase.iid.FirebaseInstanceIdService;
/**
* Created by HP on 07-09-2017.
*/
public class myInstanceService extends FirebaseInstanceIdService {
private static final String TAG=FirebaseInstanceIdService.class.getSimpleName();
#Override
public void onTokenRefresh() {
String refreshedToken = FirebaseInstanceId.getInstance().getToken();
Log.d(TAG, "Refreshed token: " + refreshedToken);
// If you want to send messages to this application instance or
// manage this apps subscriptions on the server side, send the
// Instance ID token to your app server.
sendRegistrationToServer(refreshedToken);
}
private void sendRegistrationToServer(String refreshedToken) {
}
}
Tis is my cloud function code
const functions = require('firebase-functions');
// // Create and Deploy Your First Cloud Functions
// // https://firebase.google.com/docs/functions/write-firebase-functions
//
// exports.helloWorld = functions.https.onRequest((request, response) => {
// response.send("Hello from Firebase!");
// });
// The Firebase Admin SDK to access the Firebase Realtime Database.
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
// Listens for new messages added to messages/:pushId
exports.pushNotification = functions.database.ref('/News/{pushId}').onWrite( event => {
console.log('Push notification event triggered');
// Grab the current value of what was written to the Realtime Database.
var valueObject = event.data.val();
if(valueObject.Image != null) {
valueObject.Image= "Sent you a photo!";
}
// Create a notification
const payload = {
notification: {
title:valueObject.Title,
body: valueObject.Description || valueObject.Image,
sound: "default"
},
};
//Create an options object that contains the time to live for the notification and the priority
const options = {
priority: "high",
timeToLive: 60 * 60 * 24
};
return admin.messaging().sendToTopic("pushNotifications", payload, options);
});

Get a list of all users in Firebase [duplicate]

Not sure if I am doing something wrong but using this api https://www.firebase.com/docs/security/simple-login-email-password.html I can successfully create a user - according to the return message, but I can not see that user anywhere in the Forge console. How do you know what users are registered?
Should I be taking the return user ID and creating my own user object in Firebase or is this duplication unnecessary. I do need to add some additional user properties so perhapes I will need to do this anyway.
When using email / password authentication in Firebase Authentication (previously known as Firebase SimpleLogin), your user's email and password combination is securely stored separately from the data actually stored in your Firebase.
This barrier between the data in your Firebase and your users' email / password hash combinations is by design: we want to make it easier for you to (1) develop your application, (2) prevent any accidental user credential leaks, and (3) still give you total flexibility with how to store your user data in Firebase.
That means that we only store the email address / password hash combination and nothing else, so it is up to you to decide how to store actual user data in your Firebase. As you suggested, you should be taking the user id and storing that data in your Firebase in a location such as /users/$id, and using the Firebase Security Rules Language to determine read / write access to that data. Your user's unique id and email are already in the auth variable you'll use when writing rules.
Here i created an Android program to do what Rob said for firebase beginner(like me)
out there.this program first store the username of the signedUp or signedIn user and then display them in a listView
SignInActivity.java
public class SignInActivity extends BaseActivity implements View.OnClickListener,View.OnKeyListener{
private DatabaseReference mDatabase;
public static FirebaseAuth mAuth;
private static final String TAG = "MainActivity";
EditText usernameField;
EditText passwordField;
TextView changeSignUpModeTextView;
Button signUpButton;
ImageView logo;
RelativeLayout relativeLayout;
Boolean signUpModeActive;
static ArrayList<String> userList = new ArrayList<>();
#Override
public void onStart() {
super.onStart();
// Check auth on Activity start
if (mAuth.getCurrentUser() != null) {
onAuthSuccess(mAuth.getCurrentUser());
}
}
#Override
public boolean onKey(View view, int i, KeyEvent keyEvent) {
if(i == keyEvent.KEYCODE_ENTER && keyEvent.getAction() == keyEvent.ACTION_DOWN){
signUpOrLogIn(view);
}
return false;
}
#Override
public void onClick(View view) {
if(view.getId() == R.id.changeSignUpMode){
if (signUpModeActive == true){
signUpModeActive = false;
changeSignUpModeTextView.setText("Sign Up");
signUpButton.setText("Log In");
}else{
signUpModeActive = true;
changeSignUpModeTextView.setText("Log In");
signUpButton.setText("Sign Up");
}
}else if(view.getId() == R.id.logo || view.getId() == R.id.relativeLayout){
InputMethodManager inm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
inm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(),0);
}
}
public void signUpOrLogIn(View view) {
showProgressDialog();
String email = usernameField.getText().toString().trim();
String password = passwordField.getText().toString().trim();
if (signUpModeActive == true) {
mAuth.createUserWithEmailAndPassword(email,password)
.addOnCompleteListener(MainActivity.this, new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
hideProgressDialog();
Toast.makeText(MainActivity.this, "createUserWithEmail:onComplete:" + task.isSuccessful(), Toast.LENGTH_SHORT).show();
// If sign in fails, display a message to the user. If sign in succeeds
// the auth state listener will be notified and logic to handle the
// signed in user can be handled in the listener.
if (!task.isSuccessful()) {
Toast.makeText(MainActivity.this, "Authentication failed." + task.getException().toString().substring(task.getException().toString().indexOf(" ")),
Toast.LENGTH_SHORT).show();
Log.i("Error", task.getException().toString());
} else {
onAuthSuccess(task.getResult().getUser());
showUserList();
}
}
});
} else {
mAuth.signInWithEmailAndPassword(email,password)
.addOnCompleteListener(MainActivity.this, new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
hideProgressDialog();
// If sign in fails, display a message to the user. If sign in succeeds
// the auth state listener will be notified and logic to handle the
// signed in user can be handled in the listener.
if (!task.isSuccessful()) {
// there was an error
Toast.makeText(MainActivity.this, task.getException().toString(),
Toast.LENGTH_LONG).show();
} else
{
onAuthSuccess(task.getResult().getUser());
showUserList();
}
}
});
}
}
public void showUserList(){
startActivity(new Intent(getApplicationContext(), UserList.class));
finish();
}
private void onAuthSuccess(FirebaseUser user) {
String username = usernameFromEmail(user.getEmail());
// Write new user
writeNewUser(user.getUid(), username, user.getEmail());
// Go to MainActivity
}
private String usernameFromEmail(String email) {
if (email.contains("#")) {
return email.split("#")[0];
} else {
return email;
}
}
private void writeNewUser(String userId, String name, String email) {
User user = new User(name, email);
mDatabase.child("users").child(userId).setValue(user);
ArrayList<String> userNames = new ArrayList<>();
userNames.add(name);
mDatabase.child("usernamelist").setValue(userNames);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mAuth = FirebaseAuth.getInstance();
mDatabase = FirebaseDatabase.getInstance().getReference();
if(mAuth.getCurrentUser()!=null){
showUserList();
}
usernameField = (EditText) findViewById(R.id.username);
passwordField = (EditText) findViewById(R.id.password);
changeSignUpModeTextView = (TextView) findViewById(R.id.changeSignUpMode);
signUpButton = (Button) findViewById(R.id.signupbutton);
logo = (ImageView)findViewById(R.id.logo);
relativeLayout= (RelativeLayout)findViewById(R.id.relativeLayout);
signUpModeActive = true;
changeSignUpModeTextView.setOnClickListener(this);
usernameField.setOnKeyListener(this);
passwordField.setOnKeyListener(this);
logo.setOnClickListener(this);
relativeLayout.setOnClickListener(this);
}
}
UserList.java
public class UserList extends AppCompatActivity {
private static final String TAG = "UserList" ;
private DatabaseReference userlistReference;
private ValueEventListener mUserListListener;
ArrayList<String> usernamelist = new ArrayList<>();
ArrayAdapter arrayAdapter;;
ListView userListView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_user_list);
userlistReference = FirebaseDatabase.getInstance().getReference().child("usernamelist");
onStart();
userListView = (ListView) findViewById(R.id.userlistview);
}
#Override
protected void onStart() {
super.onStart();
final ValueEventListener userListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
usernamelist = new ArrayList<>((ArrayList) dataSnapshot.getValue());
usernamelist.remove(usernameOfCurrentUser());
Log.i(TAG, "onDataChange: "+usernamelist.toString());
arrayAdapter = new ArrayAdapter(UserList.this,android.R.layout.simple_list_item_1,usernamelist);
userListView.setAdapter(arrayAdapter);
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.w(TAG, "onCancelled: ",databaseError.toException());
Toast.makeText(UserList.this, "Failed to load User list.",
Toast.LENGTH_SHORT).show();
}
};
userlistReference.addValueEventListener(userListener);
mUserListListener = userListener;
}
public String usernameOfCurrentUser()
{
String email = MainActivity.mAuth.getCurrentUser().getEmail();
if (email.contains("#")) {
return email.split("#")[0];
} else {
return email;
}
}
#Override
public void onStop() {
super.onStop();
// Remove post value event listener
if (mUserListListener != null) {
userlistReference.removeEventListener(mUserListListener);
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()) {
case R.id.action_logout:
FirebaseAuth.getInstance().signOut();
startActivity(new Intent(this, MainActivity.class));
finish();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
}
You can use Google Identity Toolkit API to get a list of all registered users in your Firebase project, this API is used by the Firebase CLI which can be accessed by running firebase auth:export results-file
Make sure Identity Toolkit API is enabled
firebase-users-list.js
const serviceAccount = require('path/to/firebase-sdk-json-service-account');
const googleapis = require('googleapis');
const identitytoolkit = googleapis.identitytoolkit('v3');
const authClient = new googleapis.auth.JWT(
serviceAccount.client_email,
null,
serviceAccount.private_key,
['https://www.googleapis.com/auth/firebase'],
null
);
authClient.authorize((err) => {
if (err) {
return console.error(err);
}
let nextPageToken = undefined;
let users = [];
const getAccounts = () => identitytoolkit.relyingparty.downloadAccount({
auth: authClient,
resource: {
targetProjectId: serviceAccount.project_id,
maxResults: 200,
nextPageToken: nextPageToken
}
}, (e, results) => {
if (e) {
return console.error(err);
}
users = users.concat(results.users);
if (results.nextPageToken) {
nextPageToken = results.nextPageToken;
return getAccounts();
} else {
console.log(users);
}
});
getAccounts();
});
It's possible to use cloud function to fetch users list (view docs at firebase). Note, in the following example custom claims feature is used to check if user has enough privileges.
// USERS: return full users list for admin
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
import * as admin from 'firebase-admin'
import * as functions from 'firebase-functions'
export const listUsers = functions.https.onCall((data, context) => {
// check if user is admin (true "admin" custom claim), return error if not
const isAdmin = context.auth.token.admin === true
if (!isAdmin) {
return { error: `Unauthorized.` }
}
return admin
.auth()
.listUsers()
.then((listUsersResult) => {
// go through users array, and deconstruct user objects down to required fields
const result = listUsersResult.users.map((user) => {
const { uid, email, photoURL, displayName, disabled } = user
return { uid, email, photoURL, displayName, disabled }
})
return { result }
})
.catch((error) => {
return { error: 'Error listing users' }
})
})
You can do it using admin.auth().listUsers
Here is the doc for this: https://firebase.google.com/docs/reference/admin/node/admin.auth.Auth.html#listusers
And an usage example: https://firebase.google.com/docs/auth/admin/manage-users#list_all_users
function listAllUsers(nextPageToken) {
// List batch of users, 1000 at a time.
admin.auth().listUsers(1000, nextPageToken)
.then(function(listUsersResult) {
listUsersResult.users.forEach(function(userRecord) {
console.log('user', userRecord.toJSON());
});
if (listUsersResult.pageToken) {
// List next batch of users.
listAllUsers(listUsersResult.pageToken);
}
})
.catch(function(error) {
console.log('Error listing users:', error);
});
}
// Start listing users from the beginning, 1000 at a time.
listAllUsers();
i will answer it simply as much as possible
just add the registered user to your data base by using the following code
you can also use shared prefernces to save the data locally but it won't be able for other user.
once you save the list of user in the database simply retrieve it from there using adapters
FirebaseDatabase.getInstance().getReference().child("my_user")
.child(task.getResult().getUser().getUid())
.child("username").setValue(autoCompleteTextView1.getText().toString());
Users list in python:
from firebase_admin import credentials, db, auth
cred = credentials.Certificate('\path\to\serviceAccountKey.json')
default_app = firebase_admin.initialize_app(cred, {
"databaseURL": "https://data_base_url.firebaseio.com"
})
users = auth.list_users()

Categories