AWS Node JS MFA Cognito - aws-sdk

I was working on aws sdk for node js, and trying to authenticate an user from a particular user pool. NOTE : my user pool has Multi-factor authentication enabled and receives OTP via SMS.
This is my piece of code : ` var userData = {
Username : 'username',
Pool : userPool
};
cognitoUser = new AmazonCognitoIdentity.CognitoUser(userData);
var authenticationData = {
Username : 'username',
Password : 'password',
};
var authenticationDetails = new AmazonCognitoIdentity.AuthenticationDetails(authenticationData);
cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: function (result) {
console.log('authentication successful!')
},
onFailure: function(err) {
alert(err);
},
mfaRequired: function(codeDeliveryDetails) {
var verificationCode = prompt('Please input verification code' ,'');
cognitoUser.sendMFACode(verificationCode, this);
}
});`
BUT : the problem is : It gives an error :
Error => {"code":"UnknownError",
"message":"Unknown error, the response body from fetch is undefined"}
**And On Stack Trace I Got : ** Stack Trace : Error
at Object.onFailure (E:\Karma\node_aws\medium_try\index.js:78:79)
at E:\Karma\node_aws\medium_try\node_modules\amazon-cognito-identity-js\lib\CognitoUser.js:376:31
at E:\Karma\node_aws\medium_try\node_modules\amazon-cognito-identity-js\lib\CognitoUser.js:361:22
at E:\Karma\node_aws\medium_try\node_modules\amazon-cognito-identity-js\lib\Client.js:114:14
at <anonymous>
at process._tickDomainCallback (internal/process/next_tick.js:228:7)
**BUT AGAIN :::: **The OTP comes to my mobile...
Please can anyone help me????
Thanx in advance

Add the missed callbacks functions so you can handle the state correctly:
export interface IAuthenticationCallback {
onSuccess: (session: CognitoUserSession, userConfirmationNecessary?: boolean) => void,
onFailure: (err: any) => void,
newPasswordRequired?: (userAttributes: any, requiredAttributes: any) => void,
mfaRequired?: (challengeName: any, challengeParameters: any) => void,
totpRequired?: (challengeName: any, challengeParameters: any) => void,
customChallenge?: (challengeParameters: any) => void,
mfaSetup?: (challengeName: any, challengeParameters: any) => void,
selectMFAType?: (challengeName: any, challengeParameters: any) => void
}

global['fetch'] = require('node-fetch');
Use the above code at the top of your file.

Related

window.ethereum.providers.find((provider) => provider.isMetamask) returns undefined

Metamask wallet will not connect if coinbase is installed.
This is a critical issue for anyone trying to execute transactions on their website. Asking users to remove their coinbase wallet is a major deterrent.
https://docs.metamask.io/guide/ethereum-provider.html#using-the-provider
This link on the metamask website demonstrates that they are aware of the issue but no documentation addresses it.
This issue can't be impossible to fix because sites like uniswap allow users to select which wallet. So I think it would be helpful if this was addressed in the documentation.
This is the original code for the connect function:
const connectMetamask = async (setAlert, setCurrentAccount) => {
const {ethereum} = window
const isInstalled = () => {
return Boolean(ethereum && ethereum.isMetaMask)
}
const isConnect = () => {
return Boolean(ethereum && ethereum.isConnected()) // <-- this is the issue
}
try {
if (!isInstalled()) {
console.log("No metamask!"); // <-- this works
setAlert(true);
return;
}
if (!isConnect()) {
console.log("Metamask not connected!");
setAlert(true)
return;
}
const chainId = await ethereum.request({ method: "eth_chainId" });
} catch (error) {
console.error(error);
}
};
This code works fine to connect metamask IF coinbase wallet is not installed.
https://docs.cloud.coinbase.com/wallet-sdk/docs/injected-provider-guidance
This link suggests what to do- but it doesn't work (for me at least).
if (window.ethereum.providers?.length) {
window.ethereum.providers.forEach(async (p) => {
if (p.isMetaMask) provider = p;
});
}
window.ethereum.providers returns an array, first element being the coinbase wallet which works fine, second being a proxy containing metamask. The properties of this proxy object are not accessible.
As per some answers I've written (which is the same as the code in the coinbase example):
const metamaskProvider = await window.ethereum.providers.find((provider) => provider.isMetaMask);
ethereum.setSelectedProvider(metamaskProvider)
Logging metamaskProvider returns undefined.
Logging window.ethereum.providers returns an array:
0: v {_events: {…}, _eventsCount: 0, _maxListeners: undefined, _filterPolyfill: e.FilterPolyfill, _subscriptionManager: e.SubscriptionManager, …}
1: Proxy {_events: {…}, _eventsCount: 0, _maxListeners: 100, _log: u, _state: {…}, …}
1 is the metamask provider.
It contains the following properties:
1: Proxy
[[Handler]]: Object
deleteProperty: ()=>!0
[[Prototype]]: Object
[[Target]]: l
chainId: "0x1"
enable: ƒ ()
isMetaMask: true
....
metamaskProvider returns undefined. How do I access the isMetaMask property, and set my selectedProvider to metamaskProvider ?
I think if you have multiple wallets are installed, you would not have window.ethereum.providers defined. I currently have only "metamask", I get this error: Uncaught TypeError: Cannot read properties of undefined (reading 'find')
if (typeof window.ethereum !== "undefined") {
let provider = window.ethereum;
// if multiple wallet are installed
if (window.ethereum.providers?.length) {
window.ethereum.providers.find(async (p) => {
if (p.isMetaMask) provider = p;
});
}
also you should not destructure
const metamaskProvider = await window.ethereum.providers.find((provider) => provider.isMetaMask);
My fix:
const connectMetamask = async (setAlert, setCurrentAccount) => {
let { ethereum } = window;
try {
if (!ethereum) {
console.log("No metamask!");
setAlert(true);
return;
}
try{
if (!ethereum.isConnected()) {
console.log("Metamask not connected!");
setAlert(true)
return;
}
} catch (error){
console.error(error)
try{
console.log('providers',window.ethereum.providers);
console.log('ethVar',ethereum)
ethereum = await window.ethereum.providers.find(
(provider) => provider.isMetaMask );
console.log('ethVarAfterFind', ethereum)
} catch (error){
console.error(error)
}
}
const chainId = await ethereum.request({ method: "eth_chainId" });
.....
} catch (error) {
console.log('error:')
console.error(error);
}
};
export default connectMetamask;
Basically just :
ethereum = await window.ethereum.providers.find( (provider) => provider.isMetaMask );
const chainId = await ethereum.request({ method: "eth_chainId" });
Honestly I'm pretty sure I tried this already and it wasn't working but, it is now ¯\_(ツ)_/¯

cloud function Not all code paths return a value still I am returning all values possible?

any idea why my cloud function is telling my that (Not all code paths return a value) I put a return on the promise at the end also on the if statement yet it is not working, I read about similar issues tried to solve according to them but I still missing part of the puzzle, the error started showing when I wrapped the whole thing with try-catch.
export const createClient = functions.https.onCall(async(data, context) => {
try {
const snapshotCheckBelong = await db.collection('data- main').doc(data.sharedId).get();
const getClient = snapshotCheckBelong.data() ? .clients.filter((el: any) => el.clientId === data.clientId);
const snapshotCheckExist = await db.collection('data-registered-clients').doc(data.sharedId).get();
const checkClient = snapshotCheckExist.data() ? .clients.filter((el: any) => el.clientId === data.clientId);
if (checkClient[0]) {
return {
result: 'Registered client already exisits.'
};
}
if (getClient[0] ? .clientId === data.clientId && checkClient.length === 0) {
const payload = {
user: 'client',
verifiedEmail: false,
createdAt: admin.firestore.Timestamp.now(),
};
let auth = admin
.auth()
.getUserByEmail(context.auth ? .token ? .email!)
.then((user) =>
admin.auth().setCustomUserClaims(user.uid, {
userType: 'client',
premiumUnitll: admin.firestore.Timestamp.fromDate(snapshotCheckBelong.data() ? .premiumUntill)
})
)
.catch((err: any) => {
console.log(err);
});
let setClientData = db
.collection('data-clients')
.doc(context.auth ? .uid!)
.set(payload)
.catch((err: any) => {
console.log(err);
});
let updateRegisteredClients = db
.collection('data-registered-clients')
.doc(data.sharedId)
.update({
clients: admin.firestore.FieldValue.arrayUnion({
clientAccount: context.auth ? .uid,
clientId: data.clientId,
trainerId: data.sharedId
})
})
.catch((err: any) => {
console.log(err);
});
return Promise.all([auth, setClientData, updateRegisteredClients]);
} else {
return null;
}
} catch {
(err: any) => {
console.log(err);
};
}
});
Your catch blocks are not returning anything. If you handle an error with catch, you have to determine what value gets returned to the caller.
I typically prefer sending the error back to the caller, as shown in the documentation on handling errors in callable cloud functions and on the calling client.

Detect MetaMask logout (Ethereum)

I've looked at the documentation here https://metamask.github.io/metamask-docs/Main_Concepts/Getting_Started
But I'm not sure how to detect a user logging out of MetaMask?
window.ethereum.on('accountsChanged', (accounts) => {
// If user has locked/logout from MetaMask, this resets the accounts array to empty
if (!accounts.length) {
// logic to handle what happens once MetaMask is locked
}
});
Thus, using the above you can detect lock/logout of MetaMask.
window.ethereum.on('accountsChanged', function (accounts) {
let acc = accounts[0]
acc will be undefined if they logged out.
From MetaMask Ethereum Provider API:
ethereum.on('accountsChanged', handler: (accounts: Array<string>) => void);
The MetaMask provider emits this event whenever the return value of the eth_accounts RPC method changes. eth_accounts returns an array that is either empty or contains a single account address. The returned address, if any, is the address of the most recently used account that the caller is permitted to access. Callers are identified by their URL origin, which means that all sites with the same origin share the same permissions.
Metamask documentation suggest you to refresh the page if account is changed.
const setAccountListener = (provider) => {
provider.on("accountsChanged", (_) => window.location.reload());
provider.on("chainChanged", (_) => window.location.reload());
};
Then call this in useEffect
useEffect(() => {
// Load provider
if (provider) {
....
setAccountListener(provider);
// add more logic
} else {
console.error("Please, install Metamask.");
}
};
}, []);
New Feature: _metamask.isUnlocked()
Metamask adds _metamask.isUnlocked() experimental property on ethereum.
const reload = () => window.location.reload();
const handleAccount = (ethereum) => async () => {
const isLocked = !(await ethereum._metamask.isUnlocked());
if (isLocked) {
reload();
}
};
const setListener = (ethereum) => {
ethereum.on("chainChanged", reload);
ethereum.on("accountsChanged", handleAccount(ethereum));
};
const removeListener = (ethereum) => {
ethereum.removeListener("chainChanged", reload);
ethereum.removeListener("accountsChanged", handleAccount(ethereum));
};

Angular 6 AWS Cognito How to Handle newPasswordRequired

I am completely at a loss here. I have been struggling with this for several hours now trying multiple different approaches and none are getting me anywhere. My problem is I cannot seem to figure out how it the new Password is meant to be retrieved from the user within the newPasswordRequired callback after an authentication request to Cognito. Here is my code in its current state. Please don't hesitate to tell me what I can do better, as I am fairly new to Angular and completely new to using Cognito authentication.
public login(email: string, password: string): Observable<UserModel> {
const cognitoUser = new CognitoUser(this.getUserData(email));
cognitoUser.setAuthenticationFlowType('USER_PASSWORD_AUTH');
const authenticationDetails = new AuthenticationDetails(CognitoUtils.getAuthDetails(email, password));
const self = this;
return Observable.create((obs: Observer<UserModel>) => {
cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: result => {
this.session = result;
const token = result.getIdToken();
const accessToken = result.getAccessToken();
this.localStorage.setToken(token);
this.localStorage.setAccessToken(accessToken);
obs.complete();
},
onFailure: err => {
obs.error(err);
},
newPasswordRequired: (userAttributes, requiredAttributes) => {
let dialogRef: MatDialogRef<NewPasswordComponent>;
const config = new MatDialogConfig();;
config.role = 'dialog';
config.width = '40%';
config.data = { newPass: self.newPass };
dialogRef = self.dialog.open(NewPasswordComponent, config);
dialogRef.afterClosed().subscribe(result => {
self.newPass = result;
cognitoUser.completeNewPasswordChallenge(self.newPass, userAttributes, {
onSuccess: result => {
obs.complete();
},
onFailure: err => {
obs.error(err);
}
});
});
}
});
});
}
Based on what you have provided, it looks like the issue is when you respond with completeNewPasswordChallenge you're passing in userAttributes which is returned from the newPasswordRequired callback and won't work.
Instead, you need to see what attributes are required (i.e. requiredAttributes) and pass them in as an object. For example, if "name" is the required attribute, then pass in the following way:
dialogRef.afterClosed().subscribe(result => {
self.newPass = result;
cognitoUser.completeNewPasswordChallenge(self.newPass, {"name":"John Doe"}, {
onSuccess: result => {
obs.complete();
},
onFailure: err => {
obs.error(err);
}
});
Hope this helps!

TypeScript - mysql - socket.io re-emitting after failed login attempted

I am having this issue where after I fail to sign in and then successfully sign into my game it will create a game instance for every time I failed to login. This also happens on failed sign up attempts.
I loved to give you guys more code for context but stack overflow would then want me to give more detail and I'm not sure what else to say. I'll just link my repo.
One failed attempt: image
Two game instances: image
My repo: elevaidusTS
SERVER
socket.on('signIn', (signInInfo: any) => {
let sql = 'SELECT * FROM player WHERE username = ? AND password = ?';
var query = this.db.query(sql, [signInInfo.username,signInInfo.password], (err, res) => {
if (err) {
console.log(err);
socket.emit('errorFromBackend', err.code);
}else if(res.length === 0){
socket.emit('errorFromBackend', 'username and or password was incorrect');
}else{
console.log(`\n\n===============>\t Player logging in\n`)
console.log(`===============>\t username: ${signInInfo.username}\n`)
console.log(`===============>\t password: ${signInInfo.password}\n`)
this.CreatePlayer(socket, { player: res[0], isNew: false });
}
})
})
CLIENT
public SignIn(): void {
this.socket.emit('signIn', {username: signInUsername.value, password: signInPassword.value })
this.socket.on('signedIn', (playerInfo: any) => {
this.CreateGame(playerInfo);
})
this.socket.on('errorFromBackend', (err: string) => {
alert(err);
})
}
public SignUp(): void {
this.socket.emit('signUp', {username: signInUsername.value, password: signInPassword.value })
this.socket.on('signedUp', (playerInfo: any) => {
this.CreateGame(playerInfo)
})
this.socket.on('errorFromBackend', (err: string) => {
alert(err);
})
}
I was putting my listener inside the same function as the emitter. When I pressed on the signup/signin button a new instance my listener would be created so when the listener would get hit every instance of the listener would do the work.
helpful post