Ionic 3's PWA & Firebase Cloud Messaging registration - firebase-cloud-messaging

I was following this article here (which is not complete unfortunately) in attempt to learn how to friend Ionic 3 based PWA and Firebase Cloud Messaging: Push Notifications with FCM
What I did:
as advised in the article added FCM libraries into service-worker.js:
'use strict';
importScripts('./build/sw-toolbox.js');
importScripts('https://www.gstatic.com/firebasejs/4.9.0/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/4.9.0/firebase-messaging');
firebase.initializeApp({
// get this from Firebase console, Cloud messaging section
'messagingSenderId': '47286327412'
});
const messaging = firebase.messaging();
messaging.setBackgroundMessageHandler((payload) => {
console.log('Received background message ', payload);
// here you can override some options describing what's in the message;
// however, the actual content will come from the service sending messages
const notificationOptions = {
icon: '/assets/img/appicon.png'
};
return self.registration.showNotification(notificationTitle, notificationOptions);
});
self.toolbox.options.cache = {
name: 'ionic-cache'
};
// pre-cache our key assets
self.toolbox.precache(
[
'./build/main.js',
'./build/vendor.js',
'./build/main.css',
'./build/polyfills.js',
'index.html',
'manifest.json'
]
);
// dynamically cache any other local assets
self.toolbox.router.any('/*', self.toolbox.cacheFirst);
// for any other requests go to the network, cache,
// and then only use that cached resource if your user goes offline
self.toolbox.router.default = self.toolbox.networkFirst;
Then created Firebase Messaging based provider here:
import { Injectable } from "#angular/core";
import * as firebase from 'firebase';
import { Storage } from '#ionic/storage';
#Injectable()
export class FirebaseMessagingProvider {
private messaging: firebase.messaging.Messaging;
private unsubscribeOnTokenRefresh = () => {};
constructor(
private storage: Storage
) {
this.messaging = firebase.messaging();
}
public enableNotifications() {
console.log('Requesting permission...');
return this.messaging.requestPermission().then(() => {
console.log('Permission granted');
// token might change - we need to listen for changes to it and update it
this.setupOnTokenRefresh();
return this.updateToken();
});
}
public disableNotifications() {
this.unsubscribeOnTokenRefresh();
this.unsubscribeOnTokenRefresh = () => {};
return this.storage.set('fcmToken','').then();
}
private updateToken() {
return this.messaging.getToken().then((currentToken) => {
if (currentToken) {
// we've got the token from Firebase, now let's store it in the database
return this.storage.set('fcmToken', currentToken);
} else {
console.log('No Instance ID token available. Request permission to generate one.');
}
});
}
private setupOnTokenRefresh(): void {
this.unsubscribeOnTokenRefresh = this.messaging.onTokenRefresh(() => {
console.log("Token refreshed");
this.storage.set('fcmToken','').then(() => { this.updateToken(); });
});
}
}
And now during app initialization I call enableNotifications() and get error that says that default service worker is not found (404):
A bad HTTP response code (404) was received when fetching the script.
:8100/firebase-messaging-sw.js Failed to load resource: net::ERR_INVALID_RESPONSE
If I move service-worker.js firebase related stuff into default service worker in WWW folder - I get general error from Firebase (Error, failed to register service worker).
QUESTIONS:
- is there a fresh guide on Ionic 3's PWA & FCM?
- at high level what is the difference in registering service workers in Ionic 3 vs Angular? I did watch the tutorial about Angular but can't figure how to do the same in Ionic 3.

UPDATE: the below is valid as of today (02/12/2018) and most likely will be less relevant once AngularFire2 supports messaging module. So take the below with that assumption...
OK I researched and finally made it work on my Ionic 3 PWA, so I am posting solution here:
Prerequisites:
I created ionic blank app (just a home page)
installed angularfire2 and firebase ("angularfire2": "5.0.0-rc.4","firebase": "4.9.1") using npm install, I used specifically 5.0.0-rc.4" cause I had stability issues with latest one;(
created config (filename environment.ts in src folder):
export const firebaseConfig = {
apiKey: "Your Stuff Here from FB",
authDomain: "YOURAPPNAME.firebaseapp.com",
databaseURL: "https://YOURAPPNAME.firebaseio.com",
projectId: "YOURAPPNAME",
storageBucket: "YOURAPPNAME.appspot.com",
messagingSenderId: "FROMFIREBASECONEOLE"
};
I modified app.module.ts to add firebase and angularfire2 this way:
...
import { AngularFireModule } from 'angularfire2';
import 'firebase/messaging'; // only import firebase messaging or as needed;
import { firebaseConfig } from '../environment';
import { FirebaseMessagingProvider } from '../providers/firebase-messaging';
...
#NgModule({
declarations: [
MyApp,
HomePage
],
imports: [
BrowserModule,
IonicModule.forRoot(MyApp),
AngularFireModule.initializeApp(firebaseConfig),
IonicStorageModule.forRoot()
],
bootstrap: [IonicApp],
entryComponents: [
MyApp,
HomePage
],
providers: [
FirebaseMessagingProvider,
StatusBar,
SplashScreen,
{provide: ErrorHandler, useClass: IonicErrorHandler}
]
})
export class AppModule {}
Here we also import our provider whose code is below:
in providers folder I created firebase-messaging.ts like this:
import { Injectable } from "#angular/core";
import { FirebaseApp } from 'angularfire2';
// I am importing simple ionic storage (local one), in prod this should be remote storage of some sort.
import { Storage } from '#ionic/storage';
#Injectable()
export class FirebaseMessagingProvider {
private messaging;
private unsubscribeOnTokenRefresh = () => {};
constructor(
private storage: Storage,
private app: FirebaseApp
) {
this.messaging = app.messaging();
navigator.serviceWorker.register('service-worker.js').then((registration) => {
this.messaging.useServiceWorker(registration);
//this.disableNotifications()
this.enableNotifications();
});
}
public enableNotifications() {
console.log('Requesting permission...');
return this.messaging.requestPermission().then(() => {
console.log('Permission granted');
// token might change - we need to listen for changes to it and update it
this.setupOnTokenRefresh();
return this.updateToken();
});
}
public disableNotifications() {
this.unsubscribeOnTokenRefresh();
this.unsubscribeOnTokenRefresh = () => {};
return this.storage.set('fcmToken','').then();
}
private updateToken() {
return this.messaging.getToken().then((currentToken) => {
if (currentToken) {
// we've got the token from Firebase, now let's store it in the database
console.log(currentToken)
return this.storage.set('fcmToken', currentToken);
} else {
console.log('No Instance ID token available. Request permission to generate one.');
}
});
}
private setupOnTokenRefresh(): void {
this.unsubscribeOnTokenRefresh = this.messaging.onTokenRefresh(() => {
console.log("Token refreshed");
this.storage.set('fcmToken','').then(() => { this.updateToken(); });
});
}
}
Please note I init the firebase app and then in constructor we register ionic's default service worker (service-worker.js) that contains the following right after whatever is there by default:
service-worker.js:
// firebase messaging part:
importScripts('https://www.gstatic.com/firebasejs/4.9.0/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/4.9.0/firebase-messaging.js');
firebase.initializeApp({
// get this from Firebase console, Cloud messaging section
'messagingSenderId': 'YOURIDFROMYOURFIREBASECONSOLE'
});
const messaging = firebase.messaging();
messaging.setBackgroundMessageHandler(function(payload) {
console.log('Received background message ', payload);
// here you can override some options describing what's in the message;
// however, the actual content will come from the Webtask
const notificationOptions = {
icon: '/assets/images/logo-128.png'
};
return self.registration.showNotification(notificationTitle, notificationOptions);
});
At this point you also need to make sure you enabled your app as PWA, there is a good guide from Josh Morony and today there was a video stream on youtube that covers it. In TLDR you need to uncomment this in your index.html:
index.html in src uncomment:
<!-- un-comment this code to enable service worker -->
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('service-worker.js')
.then(() => console.log('service worker installed'))
.catch(err => console.error('Error', err));
}
</script>
OK almost the last thing - your manifest.json (in src) should have exact line:
"gcm_sender_id": "103953800507"
This concludes initial stuff on the client. Please note I didn't implement yet anything to handle notifications while user is in app itself, think for now it just handles when a message is sent from a server while your tab is not in focus (that is what I tested).
Now you want to go to your firebase console and obtain server key (click setting gear icon, then see cloud messaging section there). Copy server key. Also run the client (ionic serve and capture your local token (i just console.logged it). Now try sending yourself the message using a POST method. ( I did it with Postman)
// method: "POST",
//url: "https://fcm.googleapis.com/fcm/send",
// get the key from Firebase console
headers: { Authorization: `key=${fcmServerKey}` },
json: {
"notification": {
"title": "Message title",
"body": "Message body",
"click_action": "URL to your app?"
},
// userData is where your client stored the FCM token for the given user
// it should be read from the database
"to": userData.fcmRegistrationKey
}
So by doing all this I was able to reliable send myself a message WHILE the app was in background. I am yet to handle foreground but this SO question is about how to init default service worker and marry it with FCM.
I hope this will help some learners in future.

I have successfully implemented the process and got success response on API calls. But no notification popup coming on my browser. Any idea?
api: https://fcm.googleapis.com/fcm/send
response got:
{"multicast_id":6904414188195222649,"success":1,"failure":0,"canonical_ids":0,"results":[{"message_id":"0:1545375125056264%e609af1cf9fd7ecd"}]}
cheth the attached url of my console:

Related

How to use firebase push notification using laravel and vue js

I have try to make send push notification to admin user when a customer order a product.
When an admin user logged into dashboard will shown a prompt and if user subscribe the prompt user will get notification when a customer make a order in front end.I have used laravel and vue for backend.(Vue inside the laravel application).I have no idea how to prompt shown when user logged in .I have created a account in firebase and get keys.
All tutorial are based on independent vue js.In my case vue js inside the laravel app.
Where i put code which is get from firebase.?
i have tried with
//app.js
import firebase from "firebase/app";
import { initializeApp } from 'firebase/app';
import '#firebase/messaging';
var firebaseConfig = {
apiKey: "xxxxxxxxxxxxxxx",
authDomain: "xxxxxxxxx.firebaseapp.com",
projectId: "xxxxxxxxx-push",
storageBucket: "xxxxxxx.appspot.com",
messagingSenderId: "xxxxxxxxxxx",
appId: "xxxxxxxxxx:c1bae0304ce23d6eae3ccd",
measurementId: "xxxxxxx"
};
firebase.initializeApp(firebaseConfig);
// Using FCM Messaging
const messaging = firebase.messaging();
messaging.usePublicVapidKey("xxxxxxxxxxx");
// Get FCM Token
messaging.getToken().then((currentToken) => {
if (currentToken) {
console.log("Token: " + currentToken);
} else {
// Show permission request.
console.log('No Instance ID token available. Request permission to generate one.');
}
}).catch((err) => {
console.log('An error occurred while retrieving token. ', err);
});
Uncaught TypeError: firebase.messaging is not a function
at Module../resources/js/app.js
The issue is related with the version of Firebase you have installed. I think you have "firebase": "^9.X.X"
You are using the Web version 8 api. You can install v.8 if you like, but I wouldn't recommend it. V9 offers great features like tree shaking.
Here's a solution for firebase v9 :
First things first, I hope your firebaseConfig data is a dummy data, if not, you'll have to create a new firebase project because the data you just published here are sensitive and should be kept hidden.
To answer your question :
You need to import getMessaging in order to interact with FCM :
import { getMessaging, getToken } from "firebase/messaging";
Make sure your initializeApp() is inside a variable:
const app = initializeApp(firebaseConfig);
In order to initialize the messaging service :
const messaging = getMessaging(app);
Retrieving the token :
getToken(messaging, { vapidKey: '<YOUR_PUBLIC_VAPID_KEY_HERE>' }).then((currentToken) => {
if (currentToken) {
// Send the token to your server and update the UI if necessary
// ...
} 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);
// ...
});
For more info : Firebase docs

How to use Google Map API in Nuxt Js?

This is my code below to fetch API in Nuxt.Js. I have written the code that should be used to call an API, but I am not getting the results. I am not getting any resources regarding this as well.
async created(){
const config = {
headers : {
Accept : "application/json"
}
};
try{
const result = await axios.get(`https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap`, config);
console.warn(result);
//this.users = result.data;
}
catch (err){
console.warn(err);
}
},
Official GM NPM loader + diy Nuxt plugin
There's an official npm loader for the Google Maps JS API:
https://developers.google.com/maps/documentation/javascript/overview#Loading_the_Maps_API
https://www.npmjs.com/package/#googlemaps/js-api-loader
Below is how I have it implemented in Nuxt (2.15.7).
Side note: Yes, this places your API key client side, which in some contexts (e.g. internal team tools) is fine. For public production deployment, you probably want to protect the API key behind a proxy server, and keep any communication with Google occurring only on your server. A proxy server works great for things like Google search and geolocation services, however for map tiles you may never have a map tile server as fast as Google, so you may have to keep an API key on client-side to ensure smooth performance.
1. Install
npm i #googlemaps/js-api-loader
2. Make your own Nuxt plugin
plugins/mapGoogle.client.js
This keeps the Google Map API as a global so you can make use of it in various components (i.e. non-map contexts, like searching Google Places in a form).
import Vue from 'vue'
import { Loader } from '#googlemaps/js-api-loader'
// Store GM_instance as a window object (outside of the Vue context) to satisfy the GM plugin.
window.GM_instance = new Loader({
apiKey: process.env.GOOGLEMAPSAPIKEY, // This must be set in nuxt.config.js
version: "weekly",
libraries: ["places", "drawing", "geometry"] // Optional GM libraries to load
})
Vue.mixin({
data() {
return {
GM_loaded: false, // Tracks whether already GM loaded
GM_instance: null // Holds the GM instance in the context of Vue; much more convenient to use *anywhere* (Vue templates or scripts) whereas directly accessing the window object within Vue can be problematic.
GM_placeService: null, // Optional - Holds the GM Places service
}
},
methods: {
GM_load() {
return new Promise( async (resolve, reject) => {
// Need to do this only once
if (!this.GM_loaded) {
// Load the GM instance
window.GM_instance.load()
.then((response) => {
this.GM_loaded = true
// this.GM_instance is what we use to interact with GM throughout the Nuxt app
this.GM_instance = response
resolve()
})
.catch(e => {
reject(e)
})
} else {
resolve()
}
})
},
// OPTIONAL FUNCTIONS:
GM_loadPlaceService(map) {
this.GM_placeService = new this.GM_instance.maps.places.PlacesService(map)
},
GM_getPlaceDetails(placeRequest) {
return new Promise((resolve, reject) => {
this.GM_placeService.getDetails(placeRequest, (response) => {
resolve(response)
})
})
}
}
})
3. Set env and plugin in nuxt config
nuxt.config.js
Pass your GM key from your .env file and register your new plugin.
export default {
// ...
// It's best to keep your GM key where all other keys are: your .env file; however this is inaccessible client-side.
// Here, we tell Nuxt the specific env's we want to make available client-side.
env: {
GOOGLEMAPSAPIKEY: process.env.GOOGLEMAPSAPIKEY
},
// Register your new plugin
plugins: [
'#/plugins/mapGoogle.client.js',
],
// ...
}
4. Now use the GM plugin anywhere
components/map.vue
Make a map and process clicks on Google Places
<template>
<div id="map" class="map"></div>
</template>
<script>
export default {
name: "MapGoogle",
data() {
return {
map: null
}
},
mounted() {
// This is the actual trigger that loads GM dynamically.
// Here we run our global GM func: GM_load.
// Side note; annoyance: As you see, using Vue mixin's, you have functions available from out-of-nowhere. Research alternative to mixin's, especially in Vue3/Nuxt3.
this.GM_load()
.then( () => {
this.initMap()
})
},
methods: {
initMap() {
this.map = new this.GM_instance.maps.Map(document.getElementById("map"), {
center: { lat: 43.682284, lng: -79.401603 },
zoom: 8,
})
this.GM_loadPlaceService(this.map)
this.map.addListener("click", (e) => {
this.processClick(e)
})
}
},
async processClick(e) {
// If clicked target has a placeId, user has clicked a GM place
if (e.placeId) {
let placeRequest = {
placeId: e.placeId,
//fields: ['name', 'rating', 'formatted_phone_number', 'geometry']
}
// Get place details
let googlePlace = await this.GM_getPlaceDetails(placeRequest)
console.log("googlePlace %O", googlePlace)
}
}
}
</script>

auth0 receive error (Login required )when I attempt to access my auth0 protected api

I am using angular on the front end, django rest framework on the backend
I am using auth0 to handle authentication.
I had everything working fine and then I followed the docs on how to secure an api with a users auth0 credentials.
How I have this schema. A user logs in with auth0 then my system sees if a user is connected with that auth0id if not it prompts the user to create a user for my system.
So a auth0 user is attached to a system user
Regardless
when I login to my system using auth0 I console log the user object that is returned and that console log does contain the user object.
The user object token is stored in localstorage.
The auth0 auth.service is below. It is pretty standard from what came from the auth0 docs:
import { Injectable } from '#angular/core';
import createAuth0Client from '#auth0/auth0-spa-js';
import Auth0Client from '#auth0/auth0-spa-js/dist/typings/Auth0Client';
import { from, of, Observable, BehaviorSubject, combineLatest, throwError } from 'rxjs';
import { tap, catchError, concatMap, shareReplay } from 'rxjs/operators';
import { Router } from '#angular/router';
import {environment} from '../../environments/environment';
import {HttpClient} from '#angular/common/http';
#Injectable()
export class AuthService {
// Create an observable of Auth0 instance of client
auth0Client$ = (from(
createAuth0Client({
domain: environment.auth0_domain,
client_id: environment.auth0_client_id,
redirect_uri: `${window.location.origin}`,
useRefreshTokens: true,
cacheLocation: 'localstorage'
})
) as Observable<Auth0Client>).pipe(
shareReplay(1), // Every subscription receives the same shared value
catchError(err => throwError(err))
);
// Define observables for SDK methods that return promises by default
// For each Auth0 SDK method, first ensure the client instance is ready
// concatMap: Using the client instance, call SDK method; SDK returns a promise
// from: Convert that resulting promise into an observable
isAuthenticated$ = this.auth0Client$.pipe(
concatMap((client: Auth0Client) => from(client.isAuthenticated())),
tap(res => this.loggedIn = res)
);
handleRedirectCallback$ = this.auth0Client$.pipe(
concatMap((client: Auth0Client) => from(client.handleRedirectCallback()))
);
// Create subject and public observable of user profile data
private userProfileSubject$ = new BehaviorSubject<any>(null);
userProfile$ = this.userProfileSubject$.asObservable();
// Create a local property for login status
loggedIn: boolean = null;
constructor(private router: Router, private http: HttpClient) {
// On initial load, check authentication state with authorization server
// Set up local auth streams if user is already authenticated
this.localAuthSetup();
// Handle redirect from Auth0 login
this.handleAuthCallback();
}
// When calling, options can be passed if desired
// https://auth0.github.io/auth0-spa-js/classes/auth0client.html#getuser
getUser$(options?): Observable<any> {
return this.auth0Client$.pipe(
concatMap((client: Auth0Client) => from(client.getUser(options))),
tap(user => {
this.userProfileSubject$.next(user);
console.log(user);
})
);
}
// for when a user attempts to sign up by clicking the log in button vs clicking the sign up button
// these set of functions and class variables deal with that
rawsenduserobject(): Observable<any>{
return this.userProfile$;
}
// end functions and variables that deal with improper user sign up
private localAuthSetup() {
// This should only be called on app initialization
// Set up local authentication streams
const checkAuth$ = this.isAuthenticated$.pipe(
concatMap((loggedIn: boolean) => {
if (loggedIn) {
// If authenticated, get user and set in app
// NOTE: you could pass options here if needed
return this.getUser$();
}
// If not authenticated, return stream that emits 'false'
return of(loggedIn);
})
);
checkAuth$.subscribe();
}
login(redirectPath: string = '/') {
// A desired redirect path can be passed to login method
// (e.g., from a route guard)
// Ensure Auth0 client instance exists
this.auth0Client$.subscribe((client: Auth0Client) => {
// Call method to log in
client.loginWithRedirect({
redirect_uri: `${window.location.origin}`,
appState: { target: redirectPath }
});
});
}
private handleAuthCallback() {
// Call when app reloads after user logs in with Auth0
const params = window.location.search;
if (params.includes('code=') && params.includes('state=')) {
let targetRoute: string; // Path to redirect to after login processsed
const authComplete$ = this.handleRedirectCallback$.pipe(
// Have client, now call method to handle auth callback redirect
tap(cbRes => {
// Get and set target redirect route from callback results
targetRoute = cbRes.appState && cbRes.appState.target ? cbRes.appState.target : '/';
}),
concatMap(() => {
// Redirect callback complete; get user and login status
return combineLatest([
this.getUser$(),
this.isAuthenticated$
]);
})
);
// Subscribe to authentication completion observable
// Response will be an array of user and login status
authComplete$.subscribe(([user, loggedIn]) => {
// Redirect to target route after callback processing
this.router.navigate([targetRoute]);
});
}
}
logout() {
// Ensure Auth0 client instance exists
this.auth0Client$.subscribe((client: Auth0Client) => {
// Call method to log out
client.logout({
client_id: environment.auth0_client_id,
returnTo: `${window.location.origin}`
});
});
}
// for sending the user object from the backend
serversidebehaviorsubject = new BehaviorSubject(null);
sendserversideuser(user){
this.serversidebehaviorsubject.next(user);
}
receiveserversideuser():Observable<any>{
return this.serversidebehaviorsubject.asObservable();
}
}
the http interceptor again mostly from what was presented in the docs is below:
imports: [
BrowserModule,
RouterModule,
FontAwesomeModule,
RecaptchaV3Module,
FormsModule,
HttpClientModule,
AppRoutingModule,
HttpClientModule,
AuthModule.forRoot({
domain: environment.auth0_domain,
clientId: environment.auth0_client_id,
redirectUri: window.location.origin,
// The AuthHttpInterceptor configuration
httpInterceptor: {
allowedList: [
{
uri: 'http://127.0.0.1:8000/api/*'
}
],
},
})
],
providers: [Addemailtomarketinglistservice,
{ provide: HTTP_INTERCEPTORS, useClass: AuthHttpInterceptor, multi: true },
{provide: RECAPTCHA_V3_SITE_KEY, useValue: environment.recaptcha_site_key },
Googlerecaptchaservice, AuthService, HidecomponentService, S3Service, AuthClientConfig],
bootstrap: [AppComponent]
yet now whenever I call my backend api I get this error:
ERROR Error: Login required
at new n (auth0-spa-js.production.esm.js:15)
at Function.n.fromPayload (auth0-spa-js.production.esm.js:15)
at s (auth0-spa-js.production.esm.js:15)
at ZoneDelegate.invokeTask (zone-evergreen.js:399)
at Object.onInvokeTask (core.js:27492)
at ZoneDelegate.invokeTask (zone-evergreen.js:398)
at Zone.runTask (zone-evergreen.js:167)
at ZoneTask.invokeTask [as invoke] (zone-evergreen.js:480)
at invokeTask (zone-evergreen.js:1621)
at globalZoneAwareCallback (zone-evergreen.js:1658)

Cannot auth to Firebase Storage even though I'm authenticated anonymously

I have anonymous auth working.
Flutter: signed in! FirebaseUser({displayName: null, providerId: Firebase, uid: 6vj0tYIQsUSjsdiJfkjid37NhKk2, providerData: [], isEmailVerified: false, email: null, photoUrl: null, isAnonymous: true})
And my rules are as follows
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write: if request.auth != null;
}
}
}
And I keep getting this error
But if I change my rules to public
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write: if true;
}
}
}
It works!
Any idea what's going on?
This is my code. I cannot find anywhere to feed it my auth information, and I'm not sure if I even have to.
final DatabaseReference dbRef = FirebaseDatabase().reference();
final StorageReference storageRef = FirebaseStorage().ref();
final FirebaseAuth auth = FirebaseAuth.instance;
Future<void> connect() async {
this.app = await FirebaseApp.configure(
name: 'when-coin-test',
options: const FirebaseOptions(
googleAppID: '1:3739845793845:ios:9287349823487',
apiKey: 'AoijojsoafhsdiohIOSHFOSHfoiShdfoijdf',
databaseURL: 'https://when-coin-test.firebaseio.com',
storageBucket: 'gs://when-coin-test.appspot.com'
)
);
print("connected to firebase!");
this.user = await this.auth.signInAnonymously();
print("signed in! $user");
await _getAllSocialImageUrls();
print("got socialUrls! $socialUrls");
}
I think that the credentials you passed into configure are just not working.
The configuration of Firebase for Flutter is not documented everywhere. Following step 1. for Android and step 2. for iOS right here, you are already set up with your credentials.
FirebaseAuth will work with that and automatically integrate with the other Firebase plugins, like Firebase Storage.
Because of this, you can actually omit all of this:
this.app = await FirebaseApp.configure(
...
);
You can still use FirebaseApp if you would like to work with multiple projects, but for your use case, you do not need to access the class at all.
firebase_core's purpose as a package is describe on the page as follows:
Firebase Core for Flutter
A Flutter plugin to use the Firebase Core API, which enables connecting to multiple Firebase apps.
I was curious and so I went to initiate the FirebaseApp without the .configure() method (as creativecreatorormaybenot mentioned).
Doing it this way, everything works fine.
app = FirebaseApp(name: 'when-coin-test');
user = await this.auth.signInAnonymously();
This example is out of date: https://github.com/flutter/plugins/blob/master/packages/firebase_core/example/lib/main.dart
Use FirebaseAuth.fromApp(app) instead of FirebaseAuth.instance. Or you can use my simple FirebaseWrapper.
import 'dart:async';
import 'dart:io';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/material.dart';
Future<void> main() async {
await FirebaseWrapper.init();
runApp(
MyApp(),
);
}
class FirebaseWrapper {
static FirebaseAuth _auth;
static Firestore _firestore;
static FirebaseStorage _storage;
static Future<void> init() async {
WidgetsFlutterBinding.ensureInitialized();
final FirebaseApp app = await FirebaseApp.configure(
name: 'Overload',
options: FirebaseOptions(
googleAppID: Platform.isIOS ? '1:0000000000:ios:000000000000' : '1:fbfbfbfbfbfbfbbf:android:fbfbfbfbfbfbfbfbf',
gcmSenderID: '14325256252',
apiKey: 'JHasjkhvKJKhfgKfdlLDFLkDLkDJhD',
projectID: 'some-project-id',
),
);
_auth = FirebaseAuth.fromApp(app);
_firestore = Firestore(app: app);
_storage = FirebaseStorage(
app: app, storageBucket: 'gs://overload-57752.appspot.com');
}
static FirebaseAuth auth() {
return _auth;
}
static Firestore firestore() {
return _firestore;
}
static FirebaseStorage storage() {
return _storage;
}
}

Keycloak Angular 2 - Check authenticated status Keycloak object

I'm implementing the Keycloak authentication service in my Angular 2 project.
I use a service for logging in, logging out etc.
Authenticating a user and logging out seems to work. I'm now trying to protect some routes. I now have a working AuthGuard.
To check if the user is logged in (in the AuthGuard), I have a isAuthenticated() method in the service.
This is the service:
import { Injectable } from '#angular/core';
declare let Keycloak: any;
#Injectable()
export class KeycloakService {
private keycloak = new Keycloak('app/keycloak/keycloak.json');
constructor() {
this.keycloak.init({onload: 'check-sso'});
console.log(this.keycloak);
}
public login() {
this.keycloak.login();
}
public logout() {
this.keycloak.logout();
}
public isAuthenticated() {
return this.keycloak.authenticated;
}
}
Flow: User logs in, user tries to reach protected route, AuthGuard checks if user is logged in via isAuthenticated().
Note: I don't want to authenticate the user for the complete Angular app. Only for some routes.
Problem
After the user logs in, the user is redirected to the Angular app. After this, the isAuthenticated() method returns still false. Here is why:
I logged the Keycloak object to the console. I found something I didn't understand.
Keycloak object after login redirect
Same Keycloak object after login redirect (but expanded)
First the authenticated property is false. After expanding the authenticated property is true.
Question
Is the way I try to maintain my Keycloak object the correct way?
Consulted sources
https://keycloak.gitbooks.io/securing-client-applications-guide/content/v/2.5/topics/oidc/javascript-adapter.html
https://github.com/keycloak/keycloak/tree/master/examples/demo-template/angular2-product-app/src/main/webapp/app
And others
Basing on the community provided Angular2 example in keycloak's github you can spot some differences in interacting with keycloak js adapter.
Mainly the actual check on the authenticated (and possibly userName) is done on the promise returned from init.
static init(): Promise<any> {
let keycloakAuth: any = new Keycloak('keycloak.json');
KeycloakService.auth.loggedIn = false;
return new Promise((resolve, reject) => {
keycloakAuth.init({ onLoad: 'login-required' })
.success(() => {
KeycloakService.auth.loggedIn = true;
KeycloakService.auth.authz = keycloakAuth;
KeycloakService.auth.logoutUrl = keycloakAuth.authServerUrl + "/realms/demo/protocol/openid-connect/logout?redirect_uri=/angular2-product/index.html";
resolve();
})
.error(() => {
reject();
});
});
}
Also the official keycloak js adapter's documentation uses promise for the authenticated check
<head>
<script src="keycloak.js"></script>
<script>
var keycloak = Keycloak();
keycloak.init().success(function(authenticated) {
alert(authenticated ? 'authenticated' : 'not authenticated');
}).error(function() {
alert('failed to initialize');
});
</script>
</head>
If you use check-sso as a parameter to init function, the browser will be routed back to the application if the user is not logged in and will remain unauthenticated.You should use login-required instead to fix this problem.
If you don't want to authenticate the user for the complete App, you should detach the logic of creating the adapter, to make things easier if you have more than one secured component. for exemple you can create a HOC.
PS : in the example below, I am using Reactjs, I hope you can find a similar way to do this in angular:
export default (WrappedComponent) => {
return (props) => {
const [isAutenticated, setIsAutenticated] = useState(false);
const [keycloak, setKeycloak] = useState();
const loadConfig = useCallback(() => {
const keycloak = Keycloak("/keycloak.json"); //The configuration of the adapter in JSON format
keycloak.init({ onLoad: "login-required" }).then((authenticated) => {
setKeycloak(keycloak);
setIsAutenticated(authenticated);
});
}, [Keycloak]);
useEffect(() => {
loadConfig();
}, [loadConfig]);
if (keycloak) {
if (isAutenticated) {
return <WrappedComponent {...props} keycloak={keycloak} />;
} else return <AuthError message="Unable to authenticate" />;
}
return <Loader />;
};
};
you can find a useful source here