Where to put API session auth token in SDK request methods? - javascript

I am using the ConnectyCube React Native SDK and have obtained an app auth token using their API. This token is required when making further requests - for example when logging in as a user. Their documentation says:
Upgrade session token (user login)
If you have an application session token, you can upgrade it to a user session by calling login method:
var userCredentials = {login: 'cubeuser', password: 'awesomepwd'};
ConnectyCube.login(userCredentials, function(error, user) {
});
The problem is it that when I use this method, I get an error in response saying 'Token is required'.
If I were interfacing with a REST API, I would put the token in the header of the request, but obviously in this instance I can't. So the question is, where do I put the token? I have it, the documentation just doesn't tell you how to use it! Any help appreciated.

Ok I came up with a fix. First of all I just tried passing the auth token in to the userCredntials object in the same way as in the documentation for social auth, that is absent from the description in my above code snippet taken from their docs.
Then I Promisified the API calls from within useEffect inside an async function to make sure everything was happening in the right order, and it works:
export default function App() {
const createAppSession = () => {
return new Promise((resolve, reject) => {
ConnectyCube.createSession((error, session) => {
!error
? resolve(session.token)
: reject(error, '=====1=====');
});
})
}
const loginUser = (credentials) => {
return new Promise((resolve, reject) => {
ConnectyCube.login(credentials, ((error, user) => {
!error
? resolve(user)
: reject(error, '=====2=====');
}));
})
}
useEffect(() => {
const ccFunc = async () => {
ConnectyCube.init(...config)
const appSessionToken = await createAppSession();
const userCredentials = { login: 'xxxxx', password: 'xxxxxxx', keys: { token: appSessionToken } };
const user = await loginUser(userCredentials);
console.log(user);
}
ccFunc()
}, []);

Hope it works....
please implement it by yourself...just take an understanding from code below.
code says: send the username and password to api...if all ok then authenticate else throw error ...if all ok..then store the returned token is asyncStorage...you can create the storage by any name you like...and use the token eveywhere in your app.
SignInUser = async () => {
this.setState({
username: this.state.username,
password:this.state.password,
})
if(this.state.username && this.state.password !== null){
try{
this.setState({
loading:true
})
const response = await fetch('YOUR API', {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
username: this.state.username,
password: this.state.password
})
});
var promiseResponse = await response.json()
console.log(promiseResponse.token);
try {
await AsyncStorage.setItem('STORE_YOUR_LOGIN_TOKEN_HERE', JSON.stringify(promiseResponse.token));
console.log('Token Stored In Async Storage');
let tokenFromAsync = await AsyncStorage.getItem('STORE_YOUR_LOGIN_TOKEN_HERE');
console.log('Getting Token From Async...')
tokenFromAsync = JSON.parse(tokenFromAsync)
if(tokenFromAsync !== null){
console.log(tokenFromAsync);
this.setState({
loading:false
})
this.props.navigation.navigate('Tabnav');
}
} catch (error) {
// saving error
console.log(`ERROR OCCURED ${error}`)
}
//this.props.navigation.navigate('Tabnav')
} catch(error){
console.log(`COULDN'T SIGN IN ${error}`)
}
} else {
this.setState({
msg:'Invalid Credentials',
label:'red'
});
}
}

This is how i got the login to work in their sample react native app 1. i created a credentials object like this in my custom login function in src>components>AuthScreen>AuthForm.js
var credentials = {id:'',login: this.state.login,password: this.state.password}
2.I used their _signIn(credentials) function and set the 'id' attribute of my credentials object after their UserService.signin(credentials) resolved with a user object. (the resolved user object contained the logged-in user's id i.e user.id). Then it worked. This is how the code looked for the signin after the little tweak.
loginUser() { //my custom signin function
var credentials = {id:'',login: this.state.login,password: this.state.password} //my credentials object
this._signIn(credentials)
}
_signIn(userCredentials) { //their signin function
this.props.userIsLogging(true);
UserService.signin(userCredentials)
.then((user) => {
userCredentials.id = user.id //setting id of my credentials object after promise resolved
ChatService.connect(userCredentials) //using my credentials object with id value set
.then((contacts) => {
console.warn(contacts)
this.props.userLogin(user);
this.props.userIsLogging(false);
Actions.videochat(); //login worked
})
.catch(e => {
this.props.userIsLogging(false);
alert(`Error.\n\n${JSON.stringify(e)}`);
})
})
.catch(e => {
this.props.userIsLogging(false);
alert(`Error.\n\n${JSON.stringify(e)}`);
})
}

Related

userSession is null after Auth.signUp with "autoSignIn" enabled (AWS Cognito)

I need to get the jwtToken from the Auth.signUp. Is this possible if i enable autoSignIn:{enabled:true}?
const signUp = async () => {
await Auth.signUp({
username: email,
password,
attributes: {
email, // optional
name,
},
autoSignIn:{
enabled: true
}
})
.then((data) => {
console.log(data.user); //user.signInUserSession is null
})
.catch((err) => {
if (err.message) {
setInvalidMessage(err.message);
}
console.log(err);
});
await Auth.currentAuthenticatedUser()
.then(user =>{
console.log(user)
})
.catch(error => {
console.log(error) //"User is not authenticated"
})
};
I call I want the jwttoken from the userSession data for conditional rendering and I store the token in my router.js. The response object from Auth.signUp contains a CognitoUser which has a signInUserSession value but its's null.
EDIT: Tried to call Auth.currentAuthenticatedUser() after but yields an error that user is not authenticated. But when i restart my app, the user will be authenticated. I still cant authenticate user on the same app "instance"
import { Auth, Hub } from 'aws-amplify';
const listener = (data) => {
switch (data.payload.event) {
case 'autoSignIn':
console.log('auto sign in successful');
console.log(data.payload) //returns user data including session and tokens.
//other logic with user data
break;
}
};
Above is the code to initalize the Hub listener provided by amplify api. Ater user presses sign up, I called to get user session data when user is automatically signed in.
Hub.listen('auth', listener)

React Native - Direct user back to login if current user token is invalid?

I have this code that authenticates the user session by taking their token and sending it to AWS-amplify to ensure their token hasn't timed out and it's a valid user
import {Auth} from 'aws-amplify';
export async function getAccessToken() {
try {
// const currentUser = await Auth.currentAuthenticatedUser();
// console.log(currentUser);
return await Auth
.currentSession()
.then(res => {
let accessToken = res.getAccessToken();
let jwt = accessToken.getJwtToken();
// You can print them to see the full objects
// console.log(`myAccessToken: ${JSON.stringify(accessToken)}`);
// console.log(`myJwt: ${JSON.stringify(jwt)}`);
return jwt
});
} catch (error) {
console.log(error);
}
}
This gets called when make ANY endpoint call, because I want to ensure the user has been validated by aws-amplify. So for example I might have some code that does something like
function getSomething() {
getAccessToken().then(token => {
const config = {
headers: {
Authorization: `Bearer ${token}`
}
};
axios
.get(<url>, config)
.then(response => {
console.log(response.data)
setData(response.data);
})
.catch(error => {
console.log(JSON.stringify(error));
});
}).catch(error => {
console.log(JSON.stringify(error));
});;
};
So every endpoint call of my app requires the access token and if it's not authenticated properly the backend will shoot back a 401. If 401 is received I want to automatically bounce users back to login for any and all screens (except login of course)
for navigation I'm using
const NavigatorTab = ({navigation}) => {
and just setting the navigation parameter.

axios get request not working inside useEffect

I am using axios get request to check if user logged in or not with jwt, However, when app launched it keeps showing the loading state is it set to true in the first time the app launch then it supposes to make get request and validate the user then set loading state to false and navigate to a specific route. what i am getting is loading state is true all time and request not send to backend server.
here is the function to check if user logged in or not:
useEffect(() => {
const checkLoggedIn = async () => {
const Token = await AsyncStorage.getItem('Token');
if (Token) {
axios.get('http://localhost:3000/isUserAuth', {
headers: {
'x-access-token': Token
}
}).then((res) => {
setUser(res.data.user)
AsyncStorage.setItem("Token", res.token);
setIsLoading(false);
}).catch((error) => {
console.log(error)
setIsLoading(false);
});
} else {
setUser(null);
setIsLoading(false)
}
}
checkLoggedIn();
}, []);
and this is the backend:
app.get('/isUserAuth', verifyJWT, (req, res) => {
const token = req.headers['x-access-token'];
let sqlCheck = `SELECT * FROM users where id =?`;
CON.query(sqlCheck, req.user, (err, user) => {
if (user) {
console.log(user)
return res.status(400).json({ auth: true, user: user, Token: token })
}
})
})
Hope someone help me identifying the problem. thanks
If your loading state is not changing to false that signals to me that it's not a problem with your database call because even if your call is failing the else should trigger and still set loading to false.
Might consider narrowing down the function complexity and building up from there. Maybe something like the following to make sure your loading state is correctly updating:
useEffect(() => {
const checkLoggedIn = async () => {
setUser(null);
setIsLoading(false)
}
checkLoggedIn();
}, []);
What about setIsLoading to true when the component mounts, then run the async/await based on its value?
useEffect(() => {
setIsLoading(false);
if (!setIsLoading) {
try {
// fetch code
} catch(e) {
//some error handling
} finally {
// something to do whatever the outcome
}
}
})
// I found the solution
the problem is from the backend server where user returns undefined and the mistake i made that only check if(user) and didn't set else which not give a response back to font-end which indicate that state keep true
so backend code should be like:
CON.query(sqlCheck, req.user, (err, user) => {
if (user) {
return res.status(200).json({ auth: true, user: user, Token: token })
}else{ return res.status(400).json({ auth: false, user: null })}
})

Is there a way to allow headers when doing a fetch request to the Stripe Billing Portal?

I have an app that allows a logged in user to upgrade their account by subscribing to a 'basic' or 'premium' subscription. Once they subscribe they can then click on a "Manage Billing" button and it will take them to a Billing Portal where they can cancel, or upgrade their subscription.
The issue that I am running into is that I am trying to make the customer be dynamic. So where it says customer: 'cus_IZvOkC9VtDRXUi', I can instead have something like customer: customer, where customer is a dynamic variable.
router.post('/customer-portal', async (req, res) => {
// const id = req.body.id
// console.log(req.body, 'ID HERE')
// User.findBy({ id })
// .then(found => {
// console.log(found, 'I FOUND YOU')
// })
// .catch(error => {
// console.log(error, 'error here on portal')
// })
// whatever customer is logged in, this needs to be the CUSTOMER line 45 - DYNAMIC
const session = await stripe.billingPortal.sessions.create({
customer: 'cus_IZvOkC9VtDRXUi',
return_url: 'http://localhost:3000/my-profile',
})
res.redirect(session.url)
const sessionURL = session.url
console.log(sessionURL,'this is session')
// console.log(session, 'this is the session')
// console.log(req,'this is req')
// res.status(302).redirect(sessionURL)
// next()
})
The problem is that when I try and send headers (so I can send the ID of the currently logged in user ID from the frontend), the req.body is an empty object and I can't see the ID. See frontend code here:
const state = useSelector(state => state)
const id = state.auth.user.id
const handlePortal = (e) => {
e.preventDefault()
console.log(id, 'JSJDHDHDHDHDHDHDH')
return fetch('http://localhost:5000/api/stripe/customer-portal', {
// mode: 'no-cors',
method: 'post',
// headers: {
// 'Content-Type': 'application/json',
// },
body: JSON.stringify({
id: id
})
})
.then((response) => {
console.log(response, 'this is the response')
const url = response.url
console.log(url, 'URURUIRIRLL')
// express cant force the front-end to redirect.
window.location = url
// return response.json()
})
.catch((error) => {
console.log(error.message, 'There was a dumb error')
})
}
The headers are commented out right now, but as soon as I add them in, I get this error:
CORS ERROR
What is super weird is this exact setup works when I create a Stripe Customer. I send the ID from the frontend at id: currentUserId. See code below:
const state = useSelector(state => state)
const currentUserId = state.auth.user.id
const handleSubmit = e => {
e.preventDefault()
return fetch(`http://localhost:5000/api/stripe/create-customer/`, {
method: 'post',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
email: email,
id: currentUserId
})
})
.then((response) => {
console.log(response, 'this is response handlesubmit')
return response.json()
})
.then((result) => {
setCustomer(result.customer)
console.log(result, 'THIS IS RESULT on register CUSTOMER')
})
}
Then on the backend I have my /create-customer endpoint and pull the id by req.body.id, then find the currently logged in user, then add the customer id given by stripe to the user table. This works fine without any CORS issues. See backend endpoint here:
router.post('/create-customer', async (req, res) => {
const id = req.body.id
const customer = await stripe.customers.create({
email: req.body.email,
})
res.send({ customer })
User.findBy({ id })
.then(found => {
User.updateStripeID(id, changes)
.then(changedUser => {
console.log(changedUser, 'changed!', id, changes)
})
.catch(error => {
console.log(error, 'this is stupid')
})
})
.catch(error => {
console.log(error, 'there was an error on catch', error.message)
})
})
So my question is, how do I get this working? Or how do I get my currently logged in user to the backend another way?
Couple things to note:
I do have a CORS extension that I am using in Chrome. The reason is that the my frontend will not even redirect to the Billing Portal and will throw an 'access-control-allow-origin' error. I have tried every solution on every stack overflow page about this. Literally. Spent days trying almost every solution about CORS issues. They do not work and I hope that when the app deploys live it won't have that redirect issue.
If you could not already tell my frontend is React, backend is NodeJS, and I am using Knex/SQLite3 for database tables/migrations/seeds etc.
Any help would be appreciated!

How to wait for async data before send response using promises and arrow functions?

I'm new to ES6, arrow functions and promises, and I can't figure out how to use them, even worse together.
I started a project with a REST generator (https://github.com/diegohaz/rest) and it works fine, but I need to modify part of the authentication.
I need to return data from a third-party server during authentication. I created the function that returns the data correctly with axios, however I can't return this information along with the other information (from this project), response is sent before.
Below is the generated code, almost untouchable, I added just extraData: user.getExtraData(user)
// function in auth controller file
export const login = ({ user }, res, next) => {
sign(user.id)
.then((token) => ({
token, user: user.view(true), extraData: user.getExtraData(user)
}))
.then(success(res, 201))
.catch(next)
}
// function in user model file
view (full) {
let view = {}
let fields = ['id', 'name', 'picture']
if (full) {
fields = [...fields, 'email', 'createdAt']
}
fields.forEach((field) => {
view[field] = this[field]
})
return view
}
Here is my function added into the user model
getExtraData (userView) {
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded'
axios.post( userView.host, querystring.stringify( {
data1:userView.data1,
data2:userView.data2
}))
.then((response) => {
return response.data
})
.catch((error) => {
console.log('Error', error)
return null
})
}
How would the best way to make response wait until extraData is return from getExtraData function with the given code ? Thanks
You can use async/await. In that case, you need to await for getExtraData. For such reason, the anonymous function inside login and getExtraData both need to be declared as asynchronous functions:
// function in auth controller file
export const login = ({ user }, res, next) => {
sign(user.id)
.then(async (token) => ({
token,
user: user.view(true),
// Wait for getExtraData to finish using await
extraData: await user.getExtraData(user)
}))
.then(success(res, 201))
.catch(next)
}
async getExtraData (userView) {
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded'
try {
const response = await axios.post( userView.host, querystring.stringify( {
data1:userView.data1,
data2:userView.data2
}))
return response.data
}
catch (err){
return null
}
}

Categories

Resources