Building Credential object needed for Firebase Reauthentication - reactjs

When trying to build the credential object to reauthenticate a user with Firebase and ReactJS, I am getting cannot read property 'credential' of undefined where undefined is referring to this.app.auth.EmailAuthProvider which should be firebase.auth.EmailAuthProvider.
I have read that it is a static method and cannot be called on an instance, but I am not sure what exactly that means or how to correct my implementation to get the credential needed. I am calling the method in a class based component, but I am still unaware of how all of this ties in to calling a static method.
The method that I am calling is 'this.app.auth.EmailAuthProvider.credential()'
reAuthUser = (data) => {
// console.log('email: ', data.userEmail, 'password: ', data.password);
const userCredential = this.app.auth.EmailAuthProvider.credential(
data.email,
data.password
)
// this.currentUser.reauthenticateWithCredential(data.userEmail, data.password)
// .then(function() {
// alert('user has been reauthenticated')
// }).catch(function(error) {
// console.log('reauth error: ', error)
// })
};
This is in ReactJS, in a class component. this.app is a reference to Firebase and it is called in the constructor:
constructor(props) {
super(props);
this.app = Firebase;
this.currentUser = this.app.auth().currentUser;
};
I know similar questions have been asked and answers have been approved, but they don't make much sense to me at this point.

Assuming that this.app.auth is an instance of the firebase.auth.Auth class, EmailAuthProvider won't be present on that object, as it is not part of the prototype for the firebase.auth.Auth class.
EmailAuthProvider is instead part of the firebase.auth namespace which means it can only be accessed using firebase.auth.EmailAuthProvider (note how auth is not called as a function).
If using imports, you could also use
import { auth as FirebaseAuth } from 'firebase';
FirebaseAuth.EmailAuthProvider.credential(...)

Related

How to call Firebase cloud function from React stream chat app

Long story short, I'm knew to Firebase and I need to call this cloud function: "ext-auth-chat-getStreamUserToken" . The trigger is a HTTPS request to: https://europe-west2-FIREBASE-USER-INFO/ext-auth-chat-getStreamUserToken
Context: I've built a messaging app using Stream.io, with hard-coded user data and a placeholder token. Here's the key bit of code:
useEffect(() => {
async function init() {
const chatClient = StreamChat.getInstance(apiKey)
await chatClient.connectUser(user, chatClient.devToken(user.id))
const channel = chatClient.channel("messaging", "war-chat", {
image:
"https://i.picsum.photos/id/1006/200/200.jpg?hmac=yv53p45TOMz8bY4ZXUVRMFMO0_6d5vGuoWtE2hJhxlc",
name: "Oli",
members: [user.id],
//chat description
})
await channel.watch()
setClient(chatClient)
}
init()
if (client) return () => client.disconnectUser()
}, [])
I have installed the 'authenticate with stream chat' extension on Firebase. This creates various cloud functions, include one which creates a new user on Stream when a new user is created on firebase.
The only thing I need to do is modify this bit of code, to integrate firebase with stream chat:
await chatClient.connectUser(user, chatClient.devToken(user.id))
I can sort the user object. but how do I call the cloud function to get the token?
Per the source code of the getStreamUserToken Cloud Function (linked on the extension's product page), it is a Callable Cloud Function.
export const getStreamUserToken = functions.handler.https.onCall((data, context) => { /* ... */ });
Calling the Cloud Function can be done using the Firebase Client SDKs as documented with examples here in the docs. Because your cloud function resides in the europe-west2 region (and not the default us-central1 region), you will need to specify it when getting an instance of the Functions builder class:
import { getFunctions, httpsCallable } from "firebase/functions";
const functions = getFunctions(undefined, "europe-west2") // undefined is passed in so that the default app is used
const getStreamUserToken = functions.callable("ext-auth-chat-getStreamUserToken");
const streamUserToken = await getStreamUserToken();
The revoke function is called in the same way.
const revokeStreamUserToken = functions.callable("ext-auth-chat-revokeStreamUserToken");
await revokeStreamUserToken();

ES6, React: If instance of class is exported, is it safe to assume that all imports will refer to it and that it will not be re-instatiated?

Is it safe to export like this:
class Jwt {
token: string;
constructor() {
this.token = '';
}
set(token: string) {
this.token = token;
}
get() {
return this.token;
}
}
export const jwt = new Jwt();
... and to use the imported instance (import { jwt } from 'helpers/jwt';) in different files of the application, and to rely that it will be always the same instance? Also, is it reliable that it will not be garbage-collected and re-initialized on demand?
Yes, it is safe to rely on the instance of it.
There's always a way of accessing it (via the module system), so it can't be garbage collected.

Storing CognitoUser in cache make him lose some properties

I'm currently using AWS Cognito in my application.
When a user first connects whit his account, Cognito returns NEW_PASSWORD_REQUIRED as a challenge, which is fine.
I want to redirect to a page where the user can set his new password, so I put the response from Auth.signIn in storage (I tried local storage, session storage and Cache from AWS Amplify) but when I get it back on the other page, it lose some properties and Auth.completeNewPassword returns the error : 'user.completeNewPasswordChallenge is not a function'
Login.js :
try {
var authPromise = Auth.signIn(this.state.email, this.state.password);
authPromise.then((result) => {
console.log(result);
if (result.challengeName === 'NEW_PASSWORD_REQUIRED') {
Cache.setItem("CognitoUser", result);
this.props.history.push("/login/newPassword");
}
else {
this.props.userHasAuthenticated(true);
this.props.history.push("/");
}
});
} catch (e) {
alert(e.message);
this.setState({ isLoading: false });
}
NewPassword.js :
try {
var user = Cache.getItem("CognitoUser");
if (user) {
await Auth.completeNewPassword(user, this.state.newPassword);
this.props.history.push("/");
Cache.removeItem("CognitoUser");
}
} catch (e) {
alert(e.message);
this.setState({ isChanging: false });
}
Any ideas ?
It's javascript so when you write to your localcache and serializes your result into the "CognitoUser" key , it's stored as a a string, which afterwards deserialized will be a plain old Object unaware of the original type before serialization.
Original cause is maybe that your "result" type may expose functions which are not serializable (if not a getter, or if a getter with arguments).
I suggest you to call and store all the data you want into separate keys and re-read them later.
Cache.setItem("CognitoUser", result);
Cache.setItem("CognitoUser-value-1", result.myFunction1("myArg1"));
Cache.setItem("CognitoUser-value-2", result.myFunction2("myArg2"));
// ..
var user = Cache.getItem("CognitoUser");
var myVal1 = Cache.getItem("CognitoUser-value-1");
var myVal2 = Cache.getItem("CognitoUser-value-2");
You can also keep one single key "CognitoUser" in your localStorage if you make all said functions serializable. For instance, extend the type of your result adding prototypes getter functions (no arguments), each calling and returning respective myFunctionX("myArgX") functions, so that they'll appear in the JSON.stringify process.
My work around,
So this problem troubled me for some time. Amplify Cache didn't seem to work and caching username and password is a bad idea, however my work around was just include the username and password in the Require-New-Password form, so I have 4 inputs instead of just newPassword & confirmPassword which now is username, oldPassword, newPassword, and confirmPassword.
https://github.com/aws-amplify/amplify-js/issues/1715#issuecomment-642733574

setState() causes state variable to be undefined

I am, for the most part, following this tutorial.
My Django API's set up well. I have this service function:
export default class GoalService{
getGoals() {
const url = `${API_URL}/api/goals`;
return axios.get(url).then(response => response.data);
}
}
Which is called by the componentDidMount method in my GoalList:
class GoalTable extends Component {
constructor(props) {
super(props);
this.state = {
goals: [],
now: now.getDate(),
}
}
componentDidMount() {
var self = this;
goalService.getGoals().then(function (result) {
console.log(result);
self.setState({ goals: result.data })
});
}
render() { ... }
(This is step 8 of the above-linked tutorial).
Now, when I try to use { this.state.goals.map(...) }, I get the error TypeError: this.state.goals is undefined. Looking at other threads, a lot of people seem to have had this problem—but it comes about because they've used setState() outside of the request being made and, since setState() is asynchronous, the state is set to something blank. I'm using it inside of a call to then, so I don't think that's the issue.
I tried adding a second argument to then (in case this operation wasn't successful), but, the getGoals() call is successful, and successfully prints out the JSON sent back by Django's API. Similarly, I can see that the request went as expected in the Network tab of the developer tools.
What could be going wrong here? Why isn't the state properly updating w/ the returned JSON?
As mentioned in the comments, the tutorial has a typo, which means that the code tries to access response.data.data instead of response.data.
The fix would be to remove this extra level of drilling down into the object:
componentDidMount() {
var self = this;
goalService.getGoals().then(function (result) {
self.setState({ goals: result }) // no .data
});
}
Also, note that you could make this code simpler by using arrow functions (which automatically bind the this from the place that they're defined) and the object initialization shorthand:
componentDidMount() {
// { goals } is the same as { goals: goals }
goalService.getGoals().then(goals => this.setState({ goals }));
}

Angular2 RxJS calling class function from map function

I'm new to Angular 2 and Observables so I apologise if my problem is trivial. Anyway I'm trying to test the Angular 2 HTTP Client using RxJS. Although I got it to work I need to add more logic to the service I'm currently working on. Basically I'd like to have a mapping function to convert the object I receive from the web service I'm connected to, to the model object I have in Angular.
This is the code that works:
import { Injectable } from 'angular2/core';
import { Http, Response } from 'angular2/http';
import { Observable } from 'rxjs/Observable';
import { Person } from '../models/person';
#Injectable()
export class PersonsService {
constructor(private http: Http) { }
private personsUrl = 'http://localhost/api/persons';
getPersons(): Observable<Person[]> {
return this.http.get(this.personsUrl)
.map(this.extractData)
.catch(this.handleError);
}
private extractData(res: Response) {
if(res.status < 200 || res.status >= 300) {
throw new Error('Bad response status ' + res.status);
}
let body = res.json();
return body.data || {};
}
private handleError(error: any) {
let errMsg = error.message;
return Observable.throw(errMsg);
}
}
With the above code I have no problems whatsoever. The issue I'm having is that I'd like to map the object I'm getting from the service to the one I have in Angular i.e. Person. What I tried is to call another function from the extractData function that's being used by the .map function.
private extractData(res: Response) {
if(res.status < 200 || res.status >= 300) {
throw new Error('Bad response status ' + res.status);
}
let body = res.json();
// map data function
var data = this.mapData(body.data);
return data || {};
}
private mapData(data: any) {
// code to map data
}
Obviously the code above doesn't work as when this is referenced inside the extractData function, this does not refer to the PersonsService class, but it refers to a MapSubscriber object.
I don't know if it is possible to call an "external" function. It might be a silly thing but I can't find any information regarding this.
Instead of just passing the function reference use arrow functions to retain this
.map((res) => this.extractData(res))
Observable's map function allows you to pass a reference variable as a second argument on how should this actually work inside the higher-order function.
so the solution is
.map(this.extractData,this)
This way while passing the extractData function you are also passing the current class's this execution context to the higher-order function.
It will work.
Observable Doc Reference Link

Resources