Authenticating a user after metamask stopped injecting web3js into the browser - authentication

I want to create a passwordless authentication (using Metamask to sign a message; then validate the message on the server and then assign a JWT token). I want the part for signing a message to be written inside a vanilla JavaScript file.
Most of the articles I see online are from 2018 and talk about using web3.eth.personal.sign method from web3 which being injected into the browser by Metamask. However I understand this is no longer the case with Metamask. Now that web3 is not injected anymore, what functions do I call to sign a message with Metamask?
What I've attempted...
I understand there's a window.ethereum object injected into the browser but I can't seem to find an equivalent function in the Metamask documentation for web3.eth.personal.sign
I'm guessing the alternative is to use web3 without window.ethereum but how to I inject this into a vanilla JavaScript file? Also how do I ensure that the message is signed by Metamask if I just use web3 as standalone?

You could use the web3 npm package. Then you can connect it to Metamask with:
import Web3 from "web3";
window.ethereum.request({ method: "eth_requestAccounts" });
const web3 = new Web3(window.ethereum);
This will make metamask popup and ask the user to select an account.
Then to sign a message you can call web3 with:
const dataToSign = "My test message";
const accounts = await web3.eth.getAccounts();
const signature = await web3.eth.sign(dataToSign, accounts[0]);
Documentation: https://web3js.readthedocs.io/en/v1.3.4/web3-eth.html#sign
This will cause Metamask to ask the user to sign the data with their currently active account. I'm not sure how to validate if the signature is correct.

Related

Firebase functions auth.generateEmailVerificationLink() generating link with wrong apiKey

I have a Firebase functions project with dev and prod versions. There I'm using auth.generateEmailVerificationLink() to send email verification for a newly created user. Everything works well except in prod environment (testing locally or hosted) the apiKey in the link generated by auth.generateEmailVerificationLink() is not same as Firebase's default apiKey. And clicking that link I get the page with error code:
Try verifying your email again
Your request to verify your email has expired or the link has already been used
Note that when I get the link with the wrong apiKey, if I change it to the right apiKey. the verification works. So it seems the whole problem is related to the wrong apiKey in generated email verification link.
Also to note that the wrong apiKey is not random key but used in project front end for Google Maps apis.
The code itself is simple. (I'm leaving out code which creates user etc as those parts all work perfectly)
-Initializing Admin SDK:
import { initializeApp } from 'firebase-admin/app';
import { getAuth } from 'firebase-admin/auth';
initializeApp();
const auth = getAuth();
export { auth };
-Generating email verification email
const sendEmail = async () => {
const actionCodeSettings = {
// This url is working correctly, it is the same as in Firebase console
// and when changing the wrong apiKey to correct redirecting works correctly
url: process.env.DOMAIN as string,
};
await auth
.generateEmailVerificationLink(email, actionCodeSettings) // email is the email of newly created user
.then((link) => {
// generate email message with link
// generate mailOptions
// use transporter to send email
});
};
Thank you for any help
EDIT
I tested deleting that "wrong" apiKey from GCP credentials page and replaced it with another. Then running the function locally everything worked normally but the "wrong" is still in the verification email link even tho it doesn't exist anymore.
Firebase strongly recommends that if Admin SDK is used in Cloud Functions, among others, initializing the app should be done without parameters.
https://firebase.google.com/docs/admin/setup#initialize-without-parameters
For me it seems something is for some reason pulling that "wrong" and now even deleted apiKey from somewhere to usage.
I solved this by noticing that, unlike in dev project, Web Api Key (Project Settings>General) is different than Web App's firebaseConfig apiKey.
So I added correct permission to this Web Api Key (Identity Toolkit API is required for email verification email) found in GCP credentials and now the cloud function sends email verification emails with correct and working apiKey.

Authenticate Google Cloud Function Call outside GCP Environment

I am trying to call a google cloud function on the In Contact Studio to fulfill a logic in my IVR Call, but I am unable to do that as I have closed public access to my cloud function, and now I am not getting a way how to authenticate the call.
I tried using the command gcloud auth print-identity-token to get a ID_TOKEN But this ID_TOKEN will be refreshed every time and I can't use this again and again, so is there any way that I can generate a ID_TOKEN every time I try to call this function using a simple API Call??
OR
Is there any other way to solve my problem?
Ways I have Tried :-
I have gone through this Documentation:- https://cloud.google.com/functions/docs/securing/authenticating#end-users
and I was using the access style of End-User But it is a way in which the access token was getting generated via login using browser, I want to do everything via code, cause it will be used as a backend code for IVR(call facility for assistance in various tasks), in this method also we get a access token and not a ID_TOKEN, whereas to invoke a function we need a ID_TOKEN and not a access token.
Secondly I tried the gcloud auth print-identity-token command on the google cloud shell where i was logged in with my google account so it generated the JWT token and I used it as a bearer token and the function worked, but how can I generate the token outside GCP or get the on frequent intervals via code.
I want a program way(NodeJS) of doing this and not a UI way, cause I need to attach this with backend of my program, and all the ways I have gone through on the internet have the only way is through UI, and none of them have a program way for outside GCP environment, so i need help on this scenario.
As John said, you can use the Google Doc code example to perform an URL call: The library generate a secure HTTP client to generate request to your endpoint.
If you want to generate the token and use it by yourselve, you can use this piece of code
const {GoogleAuth} = require('google-auth-library');
const auth = new GoogleAuth()
auth.getIdTokenClient("").then(client => {
client.idTokenProvider.fetchIdToken("audience").then(token => {
console.log(token)
})
})

Authentication with AzureAD via TestCafe Tests

I'm unable to authenticate / sign-in via AzureAD when running testCafe.
const testrole = Role(
'https://login.microsoftonline.com/',
async t => {
await t
.typeText(Selector('input').withAttribute('type', 'email'), *******)
.click(Selector('#idSIButton9'))
.typeText(Selector('input').withAttribute('type', 'password'), ********)
.click(Selector('#idSIButton9'));
},
{ preserveUrl: true }
);
The above steps work fine, however after entering the password I get a message saying:
"Unable to sign in to Outlook account, Error: AADSTS900561: The endpoint only accepts POST requests. Received a GET request."
From my initial search, it seems like something to do with 3rd party cookies on the browser. However, I'm unable to find a solution at this time.
Any idea how I get around this issue?
The Azure AD product team has always reminded me that it is a bad idea to try to automate sign in like that.
They will probably detect that you are a bot and start blocking your requests, even if you succeed.
Instead, to acquire access tokens you need to use either the client credentials flow (for app-only tokens) or the resource owner password credentials flow (for delegated user tokens).
Client credentials flow: https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow
ROPC flow: https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth-ropc
You have to take good care to secure the credentials used for testing.
And use a test tenant if possible.

error from infura test net is empty from react-native app

I use this to connect to the infura rospten from a react-native mobile app:
const url = 'https://ropsten.infura.io/v3/xxx';
this.web3 = new Web3(new Web3.providers.HttpProvider(url))
when I call a contract, I get:
Error: Invalid JSON RPC response: ""]
why is the error empty?
Using the same method connect to the local ganache, and it works.
Is it because of the authorization or the network configuration?
You need to remove the /v3 part from your URL
Convert
const url = 'https://ropsten.infura.io/v3/xxx';
To:
const url = 'https://ropsten.infura.io/xxx';
Also, are you signing the transaction before connecting to infura. You need to sign transaction for address before making any transaction request. You can use HD-wallet-provider and use mnemonic which was provided to you during your account creation.

react-native-firebase: How to delete a token at logout?

I'd like to stop receiving notifications to the app once the user logs out.
I guess I'd have to remove the device token generated by react-native-firebase but I can't find any functionality to do this.
Does anyone know how to do this?
🔥 messaging().deleteToken()
You could achieve like this:
import auth from '#react-native-firebase/auth';
import messaging from '#react-native-firebase/messaging';
auth().onAuthStateChanged(user => {
if (!user) // Signed out
messaging().deleteToken();
});
The documentation isn't great, but I have found a working solution in v4.3.x
// login
const authorizedEntity = firebase.iid().app.options.messagingSenderId;
firebase.iid().getToken(authorizedEntity).then(token => token);
// logout
const authorizedEntity = firebase.iid().app.options.messagingSenderId;
firebase.iid().deleteToken(authorizedEntity, '*').then(nullToken => nullToken);
First of all, you shouldn't store Firebase token inside the app, you should store it in the database.
Firebase token is a device identifier for notifications, if someone steals it they could bomb someone with notifications.
Create a API route that handles logout (ex. POST /user/logout) on your backend, and on that request remove the firebase token from the database.
The firebase token is per app instance. The token will remain the same as long as the app is installed on the device. To remove the token you will have to uninstall the app.
To solve your problem, you should disocciate the token from the logged-in user when they log out. You can do this by sending a request to you server on user logout in order to update your database record where the token is associated with the user. Then, when a new user logs in, you should send another request to the server to associate the token with that user.