How to get user info with nativescript-oauth2 after user grants permission? - android

I'm working on a basic Nativescript app based on the demo on:
https://github.com/alexziskind1/nativescript-oauth2/blob/master/demo-angular/src/app/auth.service.ts
import { Injectable } from "#angular/core";
import {
TnsOAuthClient,
ITnsOAuthTokenResult
} from "nativescript-oauth2";
#Injectable()
export class AuthService {
private client: TnsOAuthClient = null;
constructor() { }
public tnsOauthLogin(providerType): Promise<ITnsOAuthTokenResult> {
this.client = new TnsOAuthClient(providerType);
return new Promise<ITnsOAuthTokenResult>((resolve, reject) => {
this.client.loginWithCompletion(
(tokenResult: ITnsOAuthTokenResult, error) => {
if (error) {
console.error("back to main page with error: ");
console.error(error);
reject(error);
} else {
console.log("back to main page with access token: ");
console.log(tokenResult);
resolve(tokenResult);
}
}
);
});
}
public tnsOauthLogout() {
if (this.client) {
this.client.logout();
}
}
}
Which gets back the access token.
My question is, how can I get the user info with that library: nativescript-oauth2?
I know that when you have the access token you can get some user info (id, name, email, etc.). For example, with Facebook you can do it in the following way by using the (dummy) access token:
https://graph.facebook.com/me?access_token=EAAF9wRREKB4BANZAZCNzp0nhmY8dgttSicR3u3aOxieEYR0kZAw298lHZAsgIwAeA9n4MeBWKivZBZB0ElFbzvo5N49kpIVozNKWFslcYIssdORsg4hHelxJI05ZBuycBjm6VrDpwWlljXkNCLR8prtdopt4mBWMYbqNouzpfn9JrF6TyXCaa2D4DhZBD4pOCBwZD
Is there any way to get the user info (id, name, email, etc.) with that library nativescript-oauth2?
How do I specify the scope of the fields I want to retrieve?, for example: { name, email, etc } by using nativescript-oauth2?
Thanks!

Related

After logging in with Google it doesn't redirect me back to my app on Android

I got an Ionic + Angular + Capacitor app with Angular Fire Auth for the Google login. My app logs me in with Google on the Web version of it, but when I export it to an Android device the Google login doesn't redirects me back to the App it just stays in the phone browser.
Any help will be appreciated.
Here's the method for the Google login:
async loginClickGoogle() {
try {
const user = await this.authService.loginGoogle();
//If the user exists
console.log('User loginclickgoogle ->', user);
if (user) {
const isVerified = this.authService.isEmailVerified(user);
if (isVerified) {
this.router.navigateByUrl('home');
this.authService.getUID();
} else {
this.emailNotVerifiedToast();
}
} else {
this.wrongUserLoginToast();
}
} catch (error) {
console.log('Error', error);
this.wrongUserLoginToast();
}
}
Here's the service method for the Google login:
async loginGoogle(): Promise<User> {
try {
const {user} = await this.afAuth.signInWithPopup(new firebase.auth.GoogleAuthProvider());
this.userUID = user.uid;
return user;
}catch (error){
console.log('Error-->',error);
}
}
When using an android device you have to use GooglePlus plugin.
Docs here : https://ionicframework.com/docs/native/google-plus
import { GooglePlus } from '#awesome-cordova-plugins/google-plus/ngx';
constructor(private googlePlus: GooglePlus) { }
...
this.googlePlus.login({})
.then(res => console.log(res))
.catch(err => console.error(err));

ReactNative - Objects are not valid as React child

I am getting this error -
"Objects are not valid as a React child (found: object with keys {$$typeof , type, key, ref, props, _owner, _store}). If you meant to render a collection of children, use an array instead"
This is specifically running the Android Emulator.
Here's my code:
export default class App extends Component {
constructor(props) {
super(props)
this.state = {
userEmail: null
}
//this.authCheck = this.authCheck.bind(this)
//this.toggleLogin = this.toggleLogin.bind(this)
this.signIn = this.signIn.bind(this)
}
componentDidMount() {
this.authListener()
}
authListener() {
firebase.auth().onAuthStateChanged((user) => {
if (user) {
console.log(user.email)
this.setState({userEmail: user.email})
} else {
console.log('no user signed in')
this.setState({userEmail: null})
}
})
}
signIn(email, password) {
//console.log(email, password)
//console.log(this.state.userEmail)
firebase.auth().signInWithEmailAndPassword(email, password)
.catch(function(error) {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
console.log(errorCode, errorMessage)
// ...
})
}
signUp(email, password) {
firebase.auth().createUserWithEmailAndPassword(email, password).catch(function(error) {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
console.log(errorCode, errorMessage)
// ...
})
}
logOut() {
firebase.auth().signOut()
}
render() {
return (
<View style={styles.container}>
{this.state.userEmail ? <ItWorked logOut={this.logOut}/> : <SignUp signIn={this.signIn} signUp={this.signUp} toggleLogin={this.toggleLogin}/>}
</View>
);
}
}
If your code is exactly as you wrote it, then ItWorked and SignUp should be undefined, which would give you an error if you try to use them in the render method.
If you did import those two components, then you might not be importing and exporting them correctly. If this is the case, you might have done something like:
// App.js
// Note that this import expects SignUp to be the default export of SignUp.js
import SignUp from './components/SignUp.js'
export default class App extends Component {
...
// SignUp.js
// Note that this is a named export of SignUp
export function SignUp() {
return <div>Lorem Ipsum</div>
}
To solve this, either export SignUp as the default:
// SignUp.js
export default function SignUp() {
return <div>Lorem Ipsum</div>
}
OR import SignUp as a named import:
// App.js
import { SignUp } from './components/SignUp.js'
...
But not both.
Another possible issue is that the rendered components (
ItWorked and SignUp) are wrapped in higher order components (like react-redux's connect), but the exporting file does not actually call the HOC.
I suspect you might find that one (or both) of those files does something like this:
// SignUp.js
function SignUp(props) {
return <div>Lorum Ipsum</div>
}
// Possible current export
export default connect(mapStateToProps)
// "Correct" export
export default connect(mapStateToProps)(SignUp)
Either way, the problem probably lies in the SignUp and ItWorks components or how they are imported by App, and it would help to get a bit more context into those components.

How to pass an object from android app to a firebase cloud function to complete Paypal payment functions?

I am using firebase cloud functions as serverside for Paypal payment. Documentations are not obvious to understand.
when I am trying to send an object from android app to firebase cloud functions, nothing has happened. I think I added it wrong. so how can I pass an object from android app to the function??
public void payout(String PayerID,String paymentId) {
// Create the arguments to the callable function.
JSONObject postData = new JSONObject();
try {
postData.put("PayerID", PayerID);
postData.put("paymentId",paymentId);
} catch (JSONException e) {
e.printStackTrace();
}
mFunctions
.getHttpsCallable("payout")
.call(postData)
.continueWith(new Continuation<HttpsCallableResult, Object>() {
#Override
public Object then(#NonNull Task<HttpsCallableResult> task)
throws Exception {
return null;
}
});
}
///////////////////////////////////////////
exports.payout=functions.https.onRequest((req,res)=>{
const sender_batch_id = Math.random().toString(36).substring(9);
const payReq=JSON.stringify({
sender_batch_header: {
sender_batch_id: sender_batch_id,
email_subject: "You have a nice payment"
},
items: [
{
recipient_type: "EMAIL",
amount: {
value: 0.90,
currency: "USD"
},
receiver: "amrmahmoudM#app.com",
note: "Thank you very much.",
sender_item_id: "item_3"
}
]
});
paypal.payout.create(payReq,(error, payout)=>{
if (error) {
console.warn(error.res);
res.status('500').end();
throw error;
}else{
console.info("payout created");
console.info(payout);
res.status('200').end();
}
});
});
exports.process = functions.https.onRequest((req, res) => {
const paymentId = req.body.paymentId;
var payerId = {
payer_id: req.body.PayerID
};
return paypal.payout.execute(paymentId, payerId, (error, payout) => {
if (error) {
console.error(error);
} else {
if (payout.state === 'approved') {
console.info('payment completed successfully, description: ',
payout.transactions[0].description);
const ref=admin.firestore().collection("Users").doc(payerId);
ref.set({'paid': true});
} else {
console.warn('payment.state: not approved ?');
}
}
}).then(r =>
console.info('promise: ', r));
});
The problem comes from the fact that in your Android app you call an HTTPS Callable Function (via mFunctions.getHttpsCallable("payout")) but your Cloud Function is not an HTTPS Callable Function but a "simple" HTTPS Function.
HTTPS Callable Functions are written like:
exports.payout = functions.https.onCall((data, context) => {
// ...
});
while HTTPS Functions are written like:
exports.payout = functions.https.onRequest((req,res)=> {
// ...
})
So you should adapt the code of your Cloud Function according to the documentation: https://firebase.google.com/docs/functions/callable
Note that another option could be to write to the database (Real Time database or Firestore) and trigger the Cloud Function with an onWrite or onCreate trigger. The advantage of this approach is that you directly save the information of the payment in the database.

Ionic 2 local storage read issue when build with '--prod'

I am building an app that does an auto login for the user when app starts if it was closed without logging out the last time. This is done by setting a local storage value at successful login and reading it in the constructor of the landing page, which is the login page.
I used ionic build android -release to build and app worked fine. But it takes about 10 sec to load. After some search, I found building the app with --prod can reduce the load time and it did reduce my app load time to 4 sec.
But now the app can't read local storage value at the start. No errors., but, it just returns the value as null. In other pages the app can read the local storage though. Looks like some needed components are not fully loaded, when the app reaches the landing page. But, by the time user logins manually those are loaded. How do I implement an auto login while reducing the load time?
import { Component } from '#angular/core';
import { NavController, MenuController, NavParams } from 'ionic-angular';
import { AuthService } from '../../providers/auth-service';
import { Global } from '../../providers/global';
import { HomePage } from '../home/home';
import { Storage } from '#ionic/storage';
import { Network } from 'ionic-native';
#Component({
selector: 'page-login',
templateUrl: 'login.html'
})
export class LoginPage {
loginUserID: string = '';
loginUserPass: string = '';
showLogin = false;
constructor(private menu: MenuController, private navCtrl: NavController, private auth: AuthService, private global: Global, private storage: Storage, private navParams: NavParams) {
this.storage.get('user_id').then((value) => {
if(this.navParams.get('showLogin')){
this.showLogin = true;
}
else{
if(value && value != null){
this.navCtrl.setRoot(HomePage);
}
else{
this.showLogin = true;
}
}
});
if(this.global.hardwareBackAction != null){
this.global.hardwareBackAction();
}
}
ionViewDidLoad() {
this.menu.swipeEnable(false, 'side-menu');
}
public login() {
if (Network.type == 'none') {
this.global.showAlert('Information', 'No internet access');
}
else {
this.global.showLoading();
this.auth.login(this.loginUserID, this.loginUserPass).subscribe(allowed => {
this.global.loading.dismiss();
if (allowed) {
setTimeout(() => {
this.storage.set('user_id', this.loginUserID);
this.navCtrl.setRoot(HomePage);
});
}
},
error => {
this.showError(error);
});
}
}
showError(text) {
setTimeout(() => {
this.global.loading.dismiss();
});
this.global.showAlert('Error', text);
}
}

Authentication with Passport Session on Android

I have a web app currently running with NodeJS and Express, where I authenticate the users using Passport sessions, and it works perfectly. Here is the overview of what I do:
app.use(session({
secret : 'hidden of course :)',
resave: false,
saveUninitialized: true
}));
app.use(passport.initialize());
app.use(passport.session());
/****** Passport functions ******/
passport.serializeUser(function (user, done) {
done(null, user.idUser);
});
passport.deserializeUser(function (id, done) {
db.user.findOne( { where : { idUser : id } }).then(function (user, err) {
done(null, user);
});
});
//Facebook
passport.use(new FacebookStrategy({
//Information stored on config/auth.js
clientID: *******,
clientSecret: ******,
callbackURL: *******,
profileFields: ['id', 'emails', 'displayName', 'name', 'gender', 'picture.type(large)']
}, function (accessToken, refreshToken, profile, done) {
//Using next tick to take advantage of async properties
process.nextTick(function () {
db.user.findOne( { where : { idUser : profile.id } }).then(function (user, err) {
if(err) {
return done(err);
}
if(user) {
return done(null, user);
} else {
// Check whether the email is undefined or valid
var emailTemp = '';
if(profile.emails && profile.emails[0] && profile.emails[0].value) {
emailTemp = profile.emails[0].value;
} else {
emailTemp = '';
}
var picture = '';
if(profile.photos && profile.photos[0] && profile.photos[0].value) {
picture = profile.photos[0].value;
} else {
picture = '/img/profile.png';
}
var sexFb = '';
if(profile.gender) {
sexFb = profile.gender;
} else {
sexFb = '';
}
// Create the user
db.user.create({
idUser : profile.id,
token : accessToken,
picture : picture,
nameUser : profile.displayName,
email : emailTemp,
sex : sexFb
}).then(function () {
db.user.findOne( { where : { idUser : profile.id } }).then(function (user, err) {
if(user) {
return done(null, user);
} else {
return done(err);
}
});
});
}
});
});
}));
app.use(express.static(__dirname + '/public/'));
/* FACEBOOK STRATEGY */
// Redirect the user to Facebook for authentication. When complete,
// Facebook will redirect the user back to the application at
// /auth/facebook/callback//
app.get('/auth/facebook', passport.authenticate('facebook', { scope : ['email']}));
/* FACEBOOK STRATEGY */
// Facebook will redirect the user to this URL after approval. Finish the
// authentication process by attempting to obtain an access token. If
// access was granted, the user will be logged in. Otherwise,
// authentication has failed.
app.get('/auth/facebook/callback',
passport.authenticate('facebook', { failureRedirect: '/' }),
function (req, res) {
// Successful authentication, redirect home.
res.redirect('../../app.html');
});
Now, I'm building our Android App and I need to authenticate our users, preferably using the backend I already built for the web version. I was checking some questions on SO like this one and I understood a lot of what I would have to do.
Currently my clients stay logged in through the cookie that Express-session handles, saving the user's id on the req.user, so that I can run queries like on this example:
app.put('/profile', function (req, res) {
//Updates the profile information of the user
db.user.update({
nameUser : req.body.nameUser
}, {
where : {
idUser : req.user.idUser
}
}).then(function (user) {
res.json({ yes : "yes" });
});
});
So my questions:
Can I authenticate my users using the same strategy as the one I currently have? If not, what would I have to change to be able to authenticate my users on Android?
Once they are authenticated, how can I set the req.user from Android (through HTTP request or whatever mean) to correctly perform the tasks on the backend (since all my requests are based on req.user)? I assume Android doesn't keep cookies like browsers do, so how would Passport Sessions know which user is calling the API?
I currently use Retrofit2 on my Android app to interact with the API, is it able to perform the actions necessary for this task?
Sorry for the long post, added a good chunk of code just to be on the safe side, if you guys have any questions or need any explanation please let me know!

Categories

Resources