How to integrate account-js package to already existed mongo db? - meteor

I have a meteor app and want to migrate non-meteor app. I found account-js is compatible with the meteor account system. When I create new user everything works well but If I want to login with already existed user account-js methods does not work and I get unauth message everytime. How to login with already existed users by using account-js ?
const accountsPassword = new AccountsPassword({
verifyPassword: (plainPassword, storedPassword) => {
const hashedPassword = crypto.Hash('sha256').update(plainPassword).digest('hex')
console.log('*****Verify Password*****')
return bcrypt.compareSync(plainPassword, storedPassword)
}
})
const accountsServer = new AccountsServer(
{
db: accountsMongo
},
{
password: accountsPassword
}
)
const accountsGraphQL = AccountsModule.forRoot({ accountsServer })
const schema = makeExecutableSchema({
typeDefs: mergeTypeDefs([typeDefs, accountsGraphQL.typeDefs]),
resolvers: mergeResolvers([resolvers, accountsGraphQL.resolvers]),
schemaDirectives: {
...accountsGraphQL.schemaDirectives
}
})
const schemaMiddleware = applyMiddleware(schema, permissions)

accounts-js utilizes an entirely different authentication mechanism than Meteor authentication system. accounts-js uses JWT tokens while Meteor uses unique hashes so it's not cross-compatible. I wrote a blog post about it which you can read here

Related

How to access google forms created by Forms API

I'm exploring the possibilities of using the Google Forms API to create Forms dynamically from my Node Express service.
After some trail and error I'm able to do a basic conversion of assessments from another system into Google forms.
But now I want to manually change the created forms and I don't know how I can access these. It's created using a service account. Can I give permissions to email addresses or something?
Accessing the Form
If you are not using Google Workspace you would need to manually share the file with the Gmail account.
Depending on which Version of Drive API you are using. You would need to use:
permissions:create
or
permissions:insert
This will allow you to create an option to directly share the file owned by the service account to another user with access to the Drive UI.
There is also a guide on how to utilize the "permission" for Drive V3 with a sample code for Node, it might give you an insight on how to follow it up:
/**
* Batch permission modification
* #param{string} fileId file ID
* #param{string} targetUserEmail username
* #param{string} targetDomainName domain
* #return{list} permission id
* */
async function shareFile(fileId, targetUserEmail, targetDomainName) {
const {GoogleAuth} = require('google-auth-library');
const {google} = require('googleapis');
// Get credentials and build service
// TODO (developer) - Use appropriate auth mechanism for your app
const auth = new GoogleAuth({
scopes: 'https://www.googleapis.com/auth/drive',
});
const service = google.drive({version: 'v3', auth});
const permissionIds = [];
const permissions = [
{
type: 'user',
role: 'writer',
emailAddress: targetUserEmail, // 'user#partner.com',
},
{
type: 'domain',
role: 'writer',
domain: targetDomainName, // 'example.com',
},
];
// Note: Client library does not currently support HTTP batch
// requests. When possible, use batched requests when inserting
// multiple permissions on the same item. For this sample,
// permissions are inserted serially.
for (const permission of permissions) {
try {
const result = await service.permissions.create({
resource: permission,
fileId: fileId,
fields: 'id',
});
permissionIds.push(result.data.id);
console.log(`Inserted permission id: ${result.data.id}`);
} catch (err) {
// TODO(developer): Handle failed permissions
console.error(err);
}
}
return permissionIds;
}
Reference
https://developers.google.com/drive/api/v2/reference/permissions/insert
https://developers.google.com/drive/api/v3/reference/permissions/create
Guide on how to manage sharing files: https://developers.google.com/drive/api/guides/manage-sharing#node.js

react native facebook login using expo client: The App_id in the input_token did not match the Viewing App

I am using expo-facebook to integrate a Facebook login using expo and firebase. Everything looks to be working and I log into Facebook but get an OAuthException once I authenticate using Facebook as follows:
Unsuccessful debug_token response from Facebook: {"error":{"message":"(#100) The App_id in the input_token did not match the Viewing App","type":"OAuthException","code":100
I have gone through a lot of issues on Stack Overflow, GitHub and looked at expo documentation as well but to no avail.
I have configured the app id and secrets from Facebook into firebase as required as well as set up the OAuth redirect URI to my Facebook app configuration. The code I have put together to setup the login is as follows:
const signInWithFacebook = async () => {
try {
// const { type, token } = await Facebook.logInWithReadPermissionsAsync(
// facebookAppId,
// {
// permissions: ["public_profile"],
// }
// );
const appId = Constants.manifest.extra.facebook.appId;
const permissions = ["public_profile"]; // Permissions required, consult Facebook docs
await Facebook.initializeAsync({
appId: appId,
});
const { type, token } = await Facebook.logInWithReadPermissionsAsync({
permissions: permissions,
});
console.log(type);
console.log(token);
if (type === "success") {
await firebase
.auth()
.setPersistence(firebase.auth.Auth.Persistence.LOCAL);
const credential = firebase.auth.FacebookAuthProvider.credential(token);
const facebookProfileData = await firebase
.auth()
.signInWithCredential(credential);
//this.onLoginSuccess.bind(this);
console.log(facebookProfileData);
}
} catch ({ message }) {
console.log(message);
alert(`Facebook Login Error: ${message}`);
}
};
I have also setup the relevant configurations in the app.json as follows:
"expo":{
"facebookScheme": "fb123243435566",
"facebookAppId": "123243435566",
"facebookDisplayName": "myapp"
}
The only aspect I am not sure about is where to grab the facebookScheme. Currenltly I have assumed it's fb+AppID. the documenatation mentioned here https://docs.expo.dev/versions/latest/sdk/facebook/
isn't clear. It states:
Configure app.json.
Add the field facebookScheme with your Facebook login redirect URL scheme found here under "4. Configure Your info.plist." It should look like "fb123456". If you do not do this, Facebook will not be able to redirect to your app after logging in.
But I am not sure how to grab that facebookScheme id. I suspect this is where the issue is as expo states that.
Expo Go from the Android Play Store will use the Facebook App ID that you provide, however, all Facebook API calls in the Expo Go from the iOS App Store will use Expo's own Facebook App ID. This is due to underlying configuration limitations.
so I am assuming the facebookScheme is some kind of workaround.
Although I am not sure if it's a working around for the ios standalone app or the expo managed.
Try this:
const { type, token } = await Expo.Facebook.logInWithReadPermissionsAsync(appId, {
permissions: [‘public_profile’, ‘email’],
behavior: Platform.OS === ‘android’ ? ‘web’ : ‘system’,
});
it should specify for the app to open a web browser on login
here's the ref:
https://forums.expo.dev/t/how-to-fix-standalone-android-expo-facebook-login/23922

Can you use IAP to log in to Firebase?

I have an angular app that is protected with Identity Aware Proxy (IAP). I am trying to add Firebase to this app in order to use firestore for a component using AngularFire. I don't want to make the user log in twice, so I thought about using IAP to authenticate with Firebase.
I've tried:
Letting GCP do its magic and see if the user is automatically inserted the Firebase Auth module - it isn't.
I've tried using the token you get from IAP in the GCP_IAAP_AUTH_TOKEN cookie with the signInWithCustomToken method - doesn't work, invalid token.
I've tried using getRedirectResult after logging in through IAP to if it's injected there - it isn't.
I've spent days trying to get this to work, I've had a colleague look at it as well, but it just doesn't seem possible. Now, as a last resort, I'm writing here to see if someone knows if it's even possible.
If not, I will have to suggest to the team to switch auth method and get rid of IAP, but I'd rather keep it.
More info:
Environment: NodeJS 10 on App Engine Flexible
Angular version: 7.x.x
AngularFire version: 5.2.3
Notes: I do not have a backend, because I want to use this component standalone and at most with a couple of Cloud Functions if need be. I am trying to use Firestore as a "backend".
I managed to authenticate on Firebase automatically using the id token from the authentication made for Cloud IAP.
I just needed to use Google API Client Library for JavaScript
1) Add the Google JS library to your page i.e. in
<script src="https://apis.google.com/js/platform.js"></script>
2) Load the OAuth2 library, gapi.auth2
gapi.load('client:auth2', callback)
gapi.auth2.init()
3) Grab the id token from GoogleAuth:
const auth = gapi.auth2.getAuthInstance()
const token = auth.currentUser.get().getAuthResponse().id_token;
4) Pass the token to GoogleAuthProvider's credential
const credential = firebase.auth.GoogleAuthProvider.credential(token);
5) Authenticate on Firebase using the credential
firebase.auth().signInAndRetrieveDataWithCredential(credential)
Putting everything together on an Angular component, this is what I have (including a sign out method)
import { Component, isDevMode, OnInit } from '#angular/core';
import { AngularFireAuth } from '#angular/fire/auth';
import { Router } from '#angular/router';
import * as firebase from 'firebase/app';
// TODO: move this all to some global state logic
#Component({
selector: 'app-sign-in-page',
templateUrl: './sign-in-page.component.html',
styleUrls: ['./sign-in-page.component.scss']
})
export class SignInPageComponent implements OnInit {
GoogleAuth?: gapi.auth2.GoogleAuth = null;
constructor(public auth: AngularFireAuth, private router: Router) { }
async ngOnInit(): Promise<void> {
// The build is restricted by Cloud IAP on non-local environments. Google
// API Client is used to take the id token from IAP's authentication and
// auto authenticate Firebase.
//
// GAPI auth: https://developers.google.com/identity/sign-in/web/reference#gapiauth2authorizeparams-callback
// GoogleAuthProvider: https://firebase.google.com/docs/reference/js/firebase.auth.GoogleAuthProvider
if (isDevMode()) return;
await this.loadGapiAuth();
this.GoogleAuth = gapi.auth2.getAuthInstance();
// Prevents a reauthentication and a redirect from `/signout` to `/dashboard` route
if (this.GoogleAuth && this.router.url === "/signin") {
const token = this.GoogleAuth.currentUser.get().getAuthResponse().id_token;
const credential = firebase.auth.GoogleAuthProvider.credential(token);
this.auth.onAuthStateChanged((user) => {
if (user) this.router.navigate(["/dashboard"]);
});
this.auth.signInAndRetrieveDataWithCredential(credential)
}
}
// Sign in button, which calls this method, should only be displayed for local
// environment where Cloud IAP isn't setup
login() {
this.auth.useDeviceLanguage();
const provider = new firebase.auth.GoogleAuthProvider();
provider.addScope("profile");
provider.addScope("email");
this.auth.signInWithRedirect(provider);
}
logout() {
this.auth.signOut();
if (this.GoogleAuth) {
// It isn't a real sign out, since there's no way yet to sign out user from IAP
// https://issuetracker.google.com/issues/69698275
// Clearing the cookie does not change the fact that the user is still
// logged into Google Accounts. When the user goes to your website again,
// opens a new tab, etc. The user is still authenticated with Google and
// therefore is still authenticated with Google IAP.
window.location.href = "/?gcp-iap-mode=CLEAR_LOGIN_COOKIE"
}
}
private async loadGapiAuth() {
await new Promise((resolve) => gapi.load('client:auth2', resolve));
await new Promise((resolve) => gapi.auth2.init(GAPI_CONFIG).then(resolve));
}
}
given the nature of IAP and Firebase, it seems not to be possible. The workaround could be just as mentioned in previous comments, to implement a custom provider, but you should mint your own token. Then maybe, re-thinking your solution if maybe this is the best way to achieve your goals.
I'm not experienced with Google Identity Aware Product, but my expectation is that you'll have to implement a custom provider for Firebase Authentication. The key part that you're missing now is a server-side code that take the information from the IAP token and mints a valid Firebase token from that. You then pass that token back to the client, which can use it to sign in with signInWithCustomToken.

How to fetch a list of 'FirebaseUser' programatically? [duplicate]

I'm working on a firebase+angularjs app and I'm using the simple email and password authentication and it's working properly.
I'm just wondering if I can add extra user data on the user table which is being used by firebase email+password auth, like I want to add billing info and other details concerning the user without creating extra node/table on firebase to store these extra data.
Firebase stores the email/password users in a separate location, that you don't have direct access to. You cannot expand the data in this location.
Since many application developers want to access the user data in their application code, it is a common practice to store all users under a /users node inside the application database itself. The disadvantage is that you have to do this yourself. But the positive side of this is that you can store any extra information if you want.
See the Firebase guide on storing user data for sample code. From there:
var ref = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com");
ref.onAuth(function(authData) {
if (authData && isNewUser) {
// save the user's profile into Firebase so we can list users,
// use them in Security and Firebase Rules, and show profiles
ref.child("users").child(authData.uid).set({
provider: authData.provider,
name: getName(authData)
});
}
});
NOTE: This method only works if you are using Firebase Admin SDK and you need to have end point on your server to manage custom tokens
Firebase Admin SDK has an option to create custom tokens with additional claims object, which can contain arbitrary data. This might be useful to store some user related info, like whether the user is premium user or not.
Additional claims data is accessible using auth object.
example
var uid = "some-uid"; //this can be existing user UID
var additionalClaims = {
premiumAccount: true,
some-user-property: 'some-value'
};
admin.auth().createCustomToken(uid, additionalClaims)
.then(function(customToken) {
// Send token back to client
})
.catch(function(error) {
console.log("Error creating custom token:", error);
});
additionalClaims are also accessible in Firebase security rules.
for more info read Firebase Custom Tokens
A Firebase User has a fixed set of basic properties—a unique ID, a primary email address, a name and a photo URL—stored in the project's user database, that can be updated by the user (iOS, Android, web). You cannot add other properties to the Firebase User object directly; instead, you can store the additional properties in your Firebase Realtime Database.
Firebase has a fixed set of user properties which can be updated but not added on to.
However you can add small amounts of data with the help of serialization and deserialization using JSON.stringify() and JSON.parse()
And then use any one of the unused properties to store the string
either in DisplayName, or photoURL property.
Keep in mind the data that can be added has to be small in size and stored as a string.
And this can be only possible with using the method in the FIREBASE SDK and not the angularfire as illustrated below
var user = firebase.auth().currentUser;
user.updateProfile({
displayName: "Jane Q. User",
photoURL: "https://example.com/jane-q-user/profile.jpg"
}).then(function() {
// Update successful.
}, function(error) {
// An error happened.
});
You could store more json like data in the photoURL or displaYName variable in the form of string here.
My answer is not angular related but I searched quiet a bit to find out how to do it using Polymer and Polymerfire so I add this answer to help people get it done faster than i did.
I had to add a separate node to db as Frank van Puffelen mentioned.
Imports:
<link rel="import" href="../bower_components/polymerfire/firebase-app.html">
<link rel="import" href="../bower_components/polymerfire/firebase-auth.html">
<link rel="import" href="../bower_components/polymerfire/firebase-document.html">
Then place anywhere in your app a <firebase-app> component:
<firebase-app
name="yourAppName"
api-key= "{{yourApi}}"
auth-domain= "{{yourAuthDomain}}"
database-url= "{{yourDbUrl}}"
>
</firebase-app>
After that you will need to use <firebase-auth> and <firebase-document>:
Template :
<firebase-auth
id="auth"
app-name="yourAppName"
signed-in="{{signedIn}}"
user="{{user}}">
</firebase-auth>
<firebase-document
id="document"
app-name="yourAppName"
path="{{usersPath}}" // e.g "/users"
data="{{userDocument}}">
</firebase-document>
Script:
this._register = function(){
var formValid = this.querySelector('#register-form').validate();
var auth = this.querySelector('#auth');
if(formValid && this.passWordsIdentic){
//The actual registration
auth.createUserWithEmailAndPassword(this.email, this.password).then(function(user){
console.log('auth user registration succes');
//Example values
this.userDocument.uid = user.uid;
this.userDocument.email = user.email;
this.userDocument.firstName = this.firstName;
this.userDocument.lastName = this.lastName;
this.userDocument.userName = this.userName;
this.$.document.save(this.usersPath).then(() => {
console.log("custom user registration succes");
this.$.document.reset();
});
}.bind(this)).catch(function(error) {
var errorCode = error.code;
var errorMessage = error.message;
console.log('error: ', errorCode);
);
}
}
And that's it, you may want to take a look at this excellent google codelab which is a good introduction into using firebase with polymer.
Here is the code of registration where add the extra fields in the Users table
import { AngularFireAuth } from "#angular/fire/auth";
constructor(private firebaseAuth: AngularFireAuth){}
registration(data: any, password: any) {
return this.firebaseAuth.auth.createUserWithEmailAndPassword(data.Email, password)
.then(res => {
res.user.updateProfile({
displayName: `${data.DisplayName}`
})
data.UserId = res.user.uid;
data.PhoneNumbers = [{
NumberType: '',
NumberValue: ''
}];
data.PhotoUrl = '';
data.Addresses = [{
AddressLine1: '',
AddressLine2: '',
City: '',
State: '',
Country: '',
PostalCode: '',
AddressType: ''
}];
data.IsDeleted = false;
this.fireStore.doc(`users/${res.user.uid}`).set(data);
this.toastr.success('User has been register successfully!', 'Successfull!');
return true;
}).catch(err => {
switch (err.code) {
case 'auth/email-already-in-use':
this.toastr.error(`Email address ${data.Email} already in use.`, 'Error!');
break;
case 'auth/invalid-email':
this.toastr.error(`Email address ${data.Email} is invalid.`, 'Error!');
break;
case 'auth/operation-not-allowed':
this.toastr.error('Error during sign up.', 'Error!');
break;
case 'auth/weak-password':
this.toastr.error('Password is not strong enough. Add additional characters including special characters and numbers.', 'Error!');
break;
default:
this.toastr.error(err.message, 'Error!');
break;
}
});
}
Here's a swift version. Your user structure ("table") is like
--users:
-------abc,d#email,com:
---------------email:abc.d#email.com
---------------name: userName
etc.
After you pass the auth FIRAuth.auth()?.createUser you can set the users in database as below:
let ref = FIRDatabase.database().reference()
let rootChild = ref.child("users")
let changedEmailChild = u.email?.lowercased().replacingOccurrences(of: ".", with: ",", options: .literal, range: nil) // Email doesn't support "," firebase doesn't support "."
let userChild = rootChild.child(changedEmailChild!)
userChild.child("email").setValue(u.email)
userChild.child("name").setValue(signup.name)
Please note that method is changed in v4.0.0. Therefore, you need to use the below code to retrieve the user profile:
afAuth.authState.subscribe((user: firebase.User) => {
this.displayName = user.displayName;
this.email = user.email;
this.photoURL = user.photoURL;
});
The answer from Frank is good, but things are a little different in Angular6/Firebase5/Angularfire5:
Here is my click handler for signing in a user:
this.afAuth.auth.signInWithPopup(new firebase.auth.GoogleAuthProvider()).then((e) => {
console.log("Log-In Success" + e.additionalUserInfo.profile.name);
if (e.additionalUserInfo.isNewUser)
this.addUserToDatabase(/*...*/);
}).catch((error) => {
console.log("Log-In Error: Google Sign-In failed");
});

Firebase - create user on Node.js server

We have a large SPA using Firebase v2. We would like to upgrade to the new API, but we experience the following problem:
As the app is quite large, we have developed many integration tests, and for these tests we always need to reset the database and initialize it to a state, where some users exist. However, we found out there really is no such thing as creating a user on server anymore ( Firebase createUserWithEmailAndPassword method is undefined in node.js ), and we are quite unsure, how to upgrade the API and yet be able to reset and initialize the database from server.
Moreover, we are quite forced to do this upgrade, because we noticed that the Firebase v2, is still using the deprecated Graph API v2.0 for Facebook OAuth, and is not recommended for use after 8.8.2016. We understand that the Firebase v2 will probably not upgrade the calls to the Graph API, as the v2 is legacy. This, however, leaves us quite cornered for now.
Any help on this topic, please?
As of Firebase v3.3.0 you are able to create user accounts using Node, but the documentation isn't great on how to expose these methods.
In order to use the user management methods, you need to initialize an application in node using your Web API key, and not the Service Account config that is walked through in the setup guide.
// The Usual Service Account Init
// This will not contain any user management methods on firebase.auth()
this.app = firebase.initializeApp(
{
serviceAccount: 'path/to/serviceaccount/file.json',
databaseURL: 'https://mydbfb.firebaseio.com'
},
'MyAppName');
// Web Client Init in Node.js
// firebase.auth() will now contain user management methods
this.app = firebase.initializeApp(
{
"apiKey": "my-api-key",
"authDomain": "somedomain.firebaseapp.com",
"databaseURL": "https://mydbfb.firebaseio.com",
"storageBucket": "myfbdb.appspot.com",
"messagingSenderId": "SomeId"
},
'MyAppName');
You can grab your client api key from your Firebase console from the Web Setup guide
https://firebase.google.com/docs/web/setup
This is the only reference I could find that explicitly referenced the need to init with api key to get this to work.
https://groups.google.com/forum/#!msg/firebase-talk/_6Rhro3zBbk/u8hB1oVRCgAJ
Given below is a working example of creating Firebase user through Node.js
exports.addUser = function(req, res) {
var wine = req.body;
var email = req.body.email;
console.log(req.body);
var password = req.body.password;
var name = req.body.name;
console.log(“Creating user for -“+email+”-“+password);
var defaultAuth = admin.auth();
admin.auth().createUser({
email: email,
emailVerified: false,
password: password,
displayName: name,
disabled: false
})
.then(function(userRecord) {
console.log(“Created Firebase User successfully with id :”, userRecord.uid);
var wine = req.body;
wine.userId = userRecord.uid;
wine.timestamp = Date.now();
delete wine.password;
status = “201”;
var reply = JSON.stringify(wine);
db.collection(‘collname’, function(err, collection) {
collection.insert(wine, {safe:true}, function(err, result) {
if (err) {
wine.status = “200”;
wine.message = “An error occured”;
reply.set(‘status’,”201″);
res.status(201).send(wine);
} else {
console.log(‘Success: ‘ + JSON.stringify(result[0]));
status= “200”;
wine.status = “200”;
wine.message = “Account created Successfully”;
res.status(200).send(wine);
}
});
});
})
.catch(function(error) {
wine.message = “An error occured—“;
wine.status = “201”;
console.log(“User Creation onf Firebase failed:”, error);
res.status(201).send(wine);
});
}
For details you can see the following blog post
http://navraj.net/?p=53
Thanks

Resources