Skip to content

Can I provide a custom persistence implementation for auth in node? #1874

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
nbransby opened this issue Jun 12, 2019 · 14 comments
Open

Can I provide a custom persistence implementation for auth in node? #1874

nbransby opened this issue Jun 12, 2019 · 14 comments

Comments

@nbransby
Copy link

I need to persist the user across sessions in node - is there to do this?

@google-oss-bot
Copy link
Contributor

I found a few problems with this issue:

  • I couldn't figure out how to label this issue, so I've labeled it for a human to triage. Hang tight.
  • This issue does not seem to follow the issue template. Make sure you provide all the required information.

@bojeil-google
Copy link
Contributor

That is not currently available. We will consider it in our modularization efforts.

@nbransby
Copy link
Author

Can you suggest a workaround? Maybe use onIdTokenChanged to save the user and updateCurrentUser on startup to restore? Problem with that is deserializing the user with the correct prototype

@JakeHedman
Copy link

JakeHedman commented Nov 2, 2019

Bit late to the party, but this workaround worked for me:

Save user:

const userJson = JSON.stringify(currentUser.toJSON())
// Write userJson to disk

Load user:

// Read userJson from disk
const userData = JSON.parse(userJson)
const user = new firebase.User(userData, userData.stsTokenManager, userData)
firebase.auth().updateCurrentUser(user)

@dfernandezm
Copy link

Bit late to the party, but this workaround worked for me:

Save user:

const userJson = JSON.stringify(currentUser.toJSON())
// Write userJson to disk

Load user:

// Read userJson from disk
const userData = JSON.parse(userJson)
const user = new firebase.User(userData, userData.stsTokenManager, userData)
firebase.auth().updateCurrentUser(user)

@JakeHedman I would love to implement this, but at least in my Typescript setup I get the following:

const user = new firebase.User(userData, userData.stsTokenManager, userData);
Property 'User' does not exist on type 'typeof firebase'.ts(2339)

Seems like I can reference the type but not instantiate it. I am using:

"firebase": "^7.14.3",
"firebase-admin": "^8.12.1",

Any workarounds on how to instantiate firebase.User that way with Typescript?

@billybraga
Copy link

Bit late to the party, but this workaround worked for me:

Save user:

const userJson = JSON.stringify(currentUser.toJSON())
// Write userJson to disk

Load user:

// Read userJson from disk
const userData = JSON.parse(userJson)
const user = new firebase.User(userData, userData.stsTokenManager, userData)
firebase.auth().updateCurrentUser(user)

Could this be documented as a public API so we can rely on it not changing in the future?

@tonyxiao
Copy link

Seems to break as of firebase version 7.21.0. Works on firebase version 7.19.1

@malcolmdeck
Copy link

Hey folks,

I've filed b/169069711 to track this feature request internally. The modularization effort is ongoing - once we complete that we should be in a place where we can consider this feature request.

Hope that helps!
~Malcolm

@mrcrowl
Copy link

mrcrowl commented Feb 1, 2021

Any workarounds on how to instantiate firebase.User that way with Typescript?

I just hit this in TypeScript too. One workaround is:

// Read userJson from disk
const userData = JSON.parse(userJson)
const user: firebase.User = new (firebase as any).User(userData, userData.stsTokenManager, userData)
firebase.auth().updateCurrentUser(user)

This is working with firebase v8.2.5

@smashah
Copy link

smashah commented Nov 4, 2021

For people using the new packages in TS:

import { getAuth, signInWithCustomToken, User } from "@firebase/auth";
import { UserImpl } from '@firebase/auth/internal';

...

const user : User = UserImpl._fromJSON(getAuth() as any, userData)
await getAuth().updateCurrentUser(user);

Just for reference, the session logs out in 55 minutes automatically

@d0gkiller87
Copy link

Dirty solution for version 9 (tested on 9.6.6):

It relies on the internal method Auth._initializeWithPersistence and persistenceManager to manually read/write the inMemoryPersistence. (this took me 5 hours reading the SDK's codebase and testing due to limited number of exports :/)

import filesystem from 'fs';

import {
  getAuth,
  inMemoryPersistence,
  updateCurrentUser
} from 'firebase/auth';

let auth = getAuth();

async load_persistence() {
  const persistence = JSON.parse( filesystem.readFileSync( 'persistence.json' ) );
  if ( !persistence ) return;

  // _initializeWithPersistence( persistenceHierarchy, popupRedirectResolver ) creates a `persistenceManager`
  await auth._initializeWithPersistence( inMemoryPersistence, null );
  // attach the persistence storage that we loaded from `persistence.json` 
  auth.persistenceManager.persistence.storage = persistence;
  // "sync" the authentication status across the whole auth module
  await updateCurrentUser( auth, await auth.persistenceManager.getCurrentUser() );
}

save_persistence() {
  filesystem.writeFileSync( 'persistence.json', JSON.stringify( auth.persistenceManager.persistence.storage ) );
}

async main() {
  await load_persistence();
  // (after signInWithEmailAndPassword, signOut, etc...)
  save_persistence();
}

@vmutafov
Copy link

As I've hit the need for having persistent auth storage for a CLI I'm building, I have written a simple npm library for creating a file-backed persistence: https://www.npmjs.com/package/@vmutafov/firebase-auth-node-persistence

Using it pretty much boils down to:

import { getApp } from "firebase/app";
import { initializeAuth } from "firebase/auth";
import { createNodeFilePersistence } from "@vmutafov/firebase-auth-node-persistence";
import { resolve } from "node:path";
import { cwd } from "node:process";

const app = getApp();

const nodeFilePersistence = createNodeFilePersistence({
    filePath: resolve(cwd(), '.authrc')
});

const auth = initializeAuth(app, {
    persistence: [nodeFilePersistence]
});

It's still something experimental and not very well tested but at least works for my use case and I hope it would help others too

@SusyVenta
Copy link

Dirty solution for version 9 (tested on 9.6.6):

It relies on the internal method Auth._initializeWithPersistence and persistenceManager to manually read/write the inMemoryPersistence. (this took me 5 hours reading the SDK's codebase and testing due to limited number of exports :/)

import filesystem from 'fs';

import {
  getAuth,
  inMemoryPersistence,
  updateCurrentUser
} from 'firebase/auth';

let auth = getAuth();

async load_persistence() {
  const persistence = JSON.parse( filesystem.readFileSync( 'persistence.json' ) );
  if ( !persistence ) return;

  // _initializeWithPersistence( persistenceHierarchy, popupRedirectResolver ) creates a `persistenceManager`
  await auth._initializeWithPersistence( inMemoryPersistence, null );
  // attach the persistence storage that we loaded from `persistence.json` 
  auth.persistenceManager.persistence.storage = persistence;
  // "sync" the authentication status across the whole auth module
  await updateCurrentUser( auth, await auth.persistenceManager.getCurrentUser() );
}

save_persistence() {
  filesystem.writeFileSync( 'persistence.json', JSON.stringify( auth.persistenceManager.persistence.storage ) );
}

async main() {
  await load_persistence();
  // (after signInWithEmailAndPassword, signOut, etc...)
  save_persistence();
}

Where do you recommend saving the file in production? And is it enough to save it once or shoudl it be one file per each user?

@d0gkiller87
Copy link

Where do you recommend saving the file in production?

It's generally not recommended to use an internal structure in production, although I understand that you may have limited options.

The choice of path is entirely up to you, for example you could save it to where you put user's data:

  • If it's an installable app, you could save it under %appdata%/xxx/ on Windows or ~/.config/xxx on Linux
  • If it's a portable app, you could save it in the same directory as the program itself

Is it enough to save it once or shoudl it be one file per each user?

The structure is specific to each user, but you have the flexibility to implement your own user management loader/saver. The structure auth.persistenceManager.persistence.storage is JSON friendly, you could make any modifications you need.

For example, unless you have security concerns, you could save multiple user's session in a single file like this:

{
  "[email protected]": /* user1's auth.persistenceManager.persistence.storage */,
  "[email protected]": /* user2's auth.persistenceManager.persistence.storage */,
  ...
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests