Skip to content

Auth middleware (beforeAuthStateChanged) #6068

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

Merged
merged 6 commits into from
Apr 13, 2022
Merged

Conversation

hsubox76
Copy link
Contributor

@hsubox76 hsubox76 commented Mar 14, 2022

Create beforeAuthStateChanged() method per internal design doc go/firebase-auth-middleware.

TODO in a separate PR: implement onAbort().

@changeset-bot
Copy link

changeset-bot bot commented Mar 14, 2022

⚠️ No Changeset found

Latest commit: ad74326

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

async signOut(): Promise<void> {
await this._runBeforeStateCallbacks(null);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will run twice since it's also in _updateCurrentUser

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like it needs to run before _setRedirectUser() so it can block it, so I added an optional arg to _updateCurrentUser() to skip running the callbacks inside it.

@@ -324,14 +325,26 @@ export class AuthImpl implements AuthInternal, _FirebaseService {
AuthErrorCode.TENANT_ID_MISMATCH
);
}
await this._runBeforeStateCallbacks(user);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_updateCurrentUser is also called by the storage onevent callbacks, which means a persistence change in another tab could trigger a stop here and the tabs would get out of sync. I'm not sure what the right behavior should be

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We discussed this, and we want the callbacks to not run if called by _onStorageEvent(), so I made use of the optional skipBeforeStateCallbacks argument I added to _updateCurrentUser when called by _onStorageEvent().

@google-oss-bot
Copy link
Contributor

google-oss-bot commented Mar 28, 2022

Size Report 1

Affected Products

  • @firebase/app

    TypeBase (927c1af)Merge (839cbfe)Diff
    browser13.8 kB13.8 kB-27 B (-0.2%)
    esm518.1 kB18.0 kB-29 B (-0.2%)
    main19.0 kB18.9 kB-58 B (-0.3%)
    module13.8 kB13.8 kB-27 B (-0.2%)
  • @firebase/app-check

    TypeBase (927c1af)Merge (839cbfe)Diff
    browser25.1 kB25.2 kB+82 B (+0.3%)
    esm529.7 kB29.8 kB+148 B (+0.5%)
    main30.9 kB31.0 kB+148 B (+0.5%)
    module25.1 kB25.2 kB+82 B (+0.3%)
  • @firebase/auth-compat

    TypeBase (927c1af)Merge (839cbfe)Diff
    browser20.1 kB20.1 kB+1 B (+0.0%)
    esm526.9 kB26.9 kB+1 B (+0.0%)
    main29.5 kB29.5 kB+1 B (+0.0%)
    module20.1 kB20.1 kB+1 B (+0.0%)
  • @firebase/auth/cordova

    TypeBase (927c1af)Merge (839cbfe)Diff
    browser180 kB182 kB+2.09 kB (+1.2%)
    module180 kB182 kB+2.09 kB (+1.2%)
  • @firebase/auth/internal

    TypeBase (927c1af)Merge (839cbfe)Diff
    browser163 kB164 kB+1.34 kB (+0.8%)
    esm5212 kB214 kB+2.09 kB (+1.0%)
    main179 kB181 kB+2.11 kB (+1.2%)
    module163 kB164 kB+1.34 kB (+0.8%)
  • @firebase/auth/react-native

    TypeBase (927c1af)Merge (839cbfe)Diff
    browser164 kB166 kB+2.11 kB (+1.3%)
    module164 kB166 kB+2.11 kB (+1.3%)
  • @firebase/firestore

    TypeBase (927c1af)Merge (839cbfe)Diff
    browser248 kB252 kB+3.30 kB (+1.3%)
    esm5309 kB313 kB+3.52 kB (+1.1%)
    main498 kB503 kB+5.51 kB (+1.1%)
    module248 kB252 kB+3.30 kB (+1.3%)
    react-native249 kB252 kB+3.30 kB (+1.3%)
  • @firebase/firestore-lite

    TypeBase (927c1af)Merge (839cbfe)Diff
    browser73.1 kB73.2 kB+60 B (+0.1%)
    esm586.5 kB86.6 kB+64 B (+0.1%)
    main126 kB126 kB+76 B (+0.1%)
    module73.1 kB73.2 kB+60 B (+0.1%)
    react-native73.3 kB73.4 kB+60 B (+0.1%)
  • @firebase/functions

    TypeBase (927c1af)Merge (839cbfe)Diff
    browser8.99 kB8.99 kB+1 B (+0.0%)
    esm511.1 kB11.1 kB+1 B (+0.0%)
    main11.8 kB11.8 kB+1 B (+0.0%)
    module8.99 kB8.99 kB+1 B (+0.0%)
  • @firebase/functions-compat

    TypeBase (927c1af)Merge (839cbfe)Diff
    browser1.67 kB1.68 kB+1 B (+0.1%)
    esm51.83 kB1.83 kB+1 B (+0.1%)
    main2.20 kB2.20 kB+1 B (+0.0%)
    module1.67 kB1.68 kB+1 B (+0.1%)
  • @firebase/installations

    TypeBase (927c1af)Merge (839cbfe)Diff
    browser17.3 kB17.8 kB+484 B (+2.8%)
    esm522.3 kB22.9 kB+628 B (+2.8%)
    main23.1 kB23.7 kB+599 B (+2.6%)
    module17.3 kB17.8 kB+484 B (+2.8%)
  • @firebase/messaging

    TypeBase (927c1af)Merge (839cbfe)Diff
    browser20.8 kB20.8 kB+21 B (+0.1%)
    esm526.1 kB26.2 kB+18 B (+0.1%)
    main26.8 kB26.8 kB-7 B (-0.0%)
    module20.8 kB20.8 kB+21 B (+0.1%)
  • @firebase/messaging-compat

    TypeBase (927c1af)Merge (839cbfe)Diff
    browser2.08 kB2.08 kB+1 B (+0.0%)
    esm52.51 kB2.51 kB+1 B (+0.0%)
    main2.90 kB2.90 kB+1 B (+0.0%)
    module2.08 kB2.08 kB+1 B (+0.0%)
  • @firebase/messaging-sw

    TypeBase (927c1af)Merge (839cbfe)Diff
    main29.6 kB29.6 kB-8 B (-0.0%)
    module22.8 kB22.8 kB+20 B (+0.1%)
  • @firebase/util

    TypeBase (927c1af)Merge (839cbfe)Diff
    browser20.5 kB23.4 kB+2.86 kB (+13.9%)
    esm521.9 kB25.5 kB+3.65 kB (+16.7%)
    main26.7 kB30.5 kB+3.82 kB (+14.3%)
    module20.5 kB23.4 kB+2.86 kB (+13.9%)
  • bundle

    43 size changes

    TypeBase (927c1af)Merge (839cbfe)Diff
    analytics (logEvent)41.8 kB40.0 kB-1.83 kB (-4.4%)
    app-check (CustomProvider)35.7 kB33.7 kB-2.03 kB (-5.7%)
    app-check (ReCaptchaEnterpriseProvider)37.9 kB35.9 kB-2.04 kB (-5.4%)
    app-check (ReCaptchaV3Provider)37.9 kB35.8 kB-2.04 kB (-5.4%)
    auth (Anonymous)65.3 kB64.1 kB-1.19 kB (-1.8%)
    auth (EmailAndPassword)69.4 kB68.2 kB-1.19 kB (-1.7%)
    auth (GoogleFBTwitterGitHubPopup)89.2 kB88.0 kB-1.19 kB (-1.3%)
    auth (GooglePopup)88.9 kB87.7 kB-1.19 kB (-1.3%)
    auth (GoogleRedirect)89.1 kB87.9 kB-1.19 kB (-1.3%)
    auth (Phone)75.4 kB74.2 kB-1.19 kB (-1.6%)
    database (Append to a list of data)146 kB143 kB-2.09 kB (-1.4%)
    database (Filtering data)144 kB142 kB-2.09 kB (-1.4%)
    database (Listen for child events)161 kB158 kB-2.09 kB (-1.3%)
    database (Listen for value events + Detach listeners)161 kB158 kB-2.09 kB (-1.3%)
    database (Listen for value events)160 kB158 kB-2.09 kB (-1.3%)
    database (Read data once)152 kB150 kB-2.09 kB (-1.4%)
    database (Save data as transactions)162 kB160 kB-2.09 kB (-1.3%)
    database (Sort data)146 kB144 kB-2.09 kB (-1.4%)
    database (Write data)145 kB143 kB-2.09 kB (-1.4%)
    firestore (Persistence)261 kB262 kB+1.06 kB (+0.4%)
    firestore (Query Cursors)203 kB202 kB-1.13 kB (-0.6%)
    firestore (Query)205 kB203 kB-1.13 kB (-0.6%)
    firestore (Read data once)193 kB192 kB-1.13 kB (-0.6%)
    firestore (Realtime updates)195 kB194 kB-1.13 kB (-0.6%)
    firestore (Transaction)178 kB176 kB-1.36 kB (-0.8%)
    firestore (Write data)177 kB176 kB-1.36 kB (-0.8%)
    firestore-lite (Query Cursors)68.3 kB66.3 kB-1.99 kB (-2.9%)
    firestore-lite (Query)71.4 kB69.4 kB-1.99 kB (-2.8%)
    firestore-lite (Read data once)55.8 kB53.8 kB-1.99 kB (-3.6%)
    firestore-lite (Transaction)73.2 kB71.2 kB-1.99 kB (-2.7%)
    firestore-lite (Write data)58.7 kB56.6 kB-2.05 kB (-3.5%)
    functions (call)29.6 kB27.6 kB-2.05 kB (-6.9%)
    messaging (send + receive)44.9 kB43.3 kB-1.63 kB (-3.6%)
    performance (trace)49.5 kB47.7 kB-1.83 kB (-3.7%)
    remote-config (getAndFetch)44.2 kB42.3 kB-1.82 kB (-4.1%)
    storage (getBytes)38.0 kB35.9 kB-2.05 kB (-5.4%)
    storage (getDownloadURL)40.0 kB38.0 kB-2.05 kB (-5.1%)
    storage (getMetadata)39.5 kB37.4 kB-2.05 kB (-5.2%)
    storage (list + listAll)38.9 kB36.9 kB-2.05 kB (-5.3%)
    storage (updateMetadata)39.8 kB37.7 kB-2.05 kB (-5.2%)
    storage (uploadBytes)44.3 kB42.2 kB-2.05 kB (-4.6%)
    storage (uploadBytesResumable)53.7 kB51.7 kB-2.05 kB (-3.8%)
    storage (uploadString)44.5 kB42.5 kB-2.05 kB (-4.6%)

  • firebase

    27 size changes

    TypeBase (927c1af)Merge (839cbfe)Diff
    firebase-analytics-compat.js26.0 kB24.3 kB-1.70 kB (-6.5%)
    firebase-analytics.js107 kB105 kB-2.09 kB (-1.9%)
    firebase-app-check-compat.js22.7 kB22.7 kB+6 B (+0.0%)
    firebase-app-check.js89.9 kB90.1 kB+129 B (+0.1%)
    firebase-app-compat.js28.3 kB26.4 kB-1.90 kB (-6.7%)
    firebase-app.js84.3 kB81.5 kB-2.78 kB (-3.3%)
    firebase-auth-compat.js123 kB124 kB+905 B (+0.7%)
    firebase-auth-cordova.js462 kB468 kB+5.42 kB (+1.2%)
    firebase-auth-react-native.js486 kB496 kB+10.4 kB (+2.1%)
    firebase-auth.js411 kB414 kB+3.05 kB (+0.7%)
    firebase-compat.js777 kB779 kB+2.48 kB (+0.3%)
    firebase-database.js603 kB603 kB+4 B (+0.0%)
    firebase-firestore-compat.js300 kB303 kB+3.16 kB (+1.1%)
    firebase-firestore-lite.js250 kB250 kB+87 B (+0.0%)
    firebase-firestore.js818 kB820 kB+2.55 kB (+0.3%)
    firebase-functions-compat.js7.95 kB7.95 kB+2 B (+0.0%)
    firebase-functions.js31.1 kB31.1 kB+3 B (+0.0%)
    firebase-messaging-compat.js38.0 kB36.4 kB-1.56 kB (-4.1%)
    firebase-messaging-sw.js102 kB101 kB-1.50 kB (-1.5%)
    firebase-messaging.js101 kB99.4 kB-1.50 kB (-1.5%)
    firebase-performance-compat.js30.8 kB29.1 kB-1.69 kB (-5.5%)
    firebase-performance-standalone-compat.es2017.js87.6 kB86.5 kB-1.10 kB (-1.3%)
    firebase-performance-standalone-compat.js65.3 kB64.0 kB-1.31 kB (-2.0%)
    firebase-performance.js119 kB117 kB-2.09 kB (-1.8%)
    firebase-remote-config-compat.js27.5 kB25.8 kB-1.70 kB (-6.2%)
    firebase-remote-config.js109 kB106 kB-2.09 kB (-1.9%)
    firebase-storage.js146 kB146 kB+1 B (+0.0%)

Test Logs

  1. https://storage.googleapis.com/firebase-sdk-metric-reports/iyJqAFdyaJ.html

@google-oss-bot
Copy link
Contributor

google-oss-bot commented Mar 28, 2022

Size Analysis Report 1

This report is too large (795,406 characters) to be displayed here in a GitHub comment. Please use the below link to see the full report on Google Cloud Storage.

Test Logs

  1. https://storage.googleapis.com/firebase-sdk-metric-reports/STM3QYScHi.html

@hsubox76 hsubox76 requested a review from jamesdaniels March 29, 2022 17:06
return this.queue(async () => {
await this.directlySetCurrentUser(user as UserInternal | null);
this.notifyAuthListeners();
});
}

async _runBeforeStateCallbacks(user: User | null): Promise<void> {
try {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method should probably check first whether or not the user is actually changing (like directlySetCurrentUser does).

Right now with this code, if you call auth.signOut() twice in a row, it will call the middleware twice in a row

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a check.

@@ -528,6 +575,7 @@ export class AuthImpl implements AuthInternal, _FirebaseService {
* should only be called from within a queued callback. This is necessary
* because the queue shouldn't rely on another queued callback.
*/
// TODO: Find where this is called and see if we can run the middleware before it
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is only used during initialization and then in a few select cases within this file. I need to do some thinking about the appropriate places in the init phase to use middleware. Off the top of my head, the only place init should run middleware is when signing in a user from redirect (i.e. first page load after a signInWithRedirect() call)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inserted it in what I think is the correct place in initializeCurrentUser().

@hsubox76 hsubox76 requested a review from allspain as a code owner April 1, 2022 18:50
@@ -223,6 +225,10 @@ export class AuthImpl implements AuthInternal, _FirebaseService {
_assert(this._popupRedirectResolver, this, AuthErrorCode.ARGUMENT_ERROR);
await this.getOrInitRedirectPersistenceManager();

// At this point in the flow, this is a redirect user. Run blocking
// middleware callbacks before setting the user.
await this._runBeforeStateCallbacks(storedUser);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think here we should wrap it in a try/catch. Initialization swallows errors. Ideally we would surface the error in getRedirectResult but that will require some extra thinking

@@ -81,6 +81,7 @@ export function applyActionCode(auth: Auth, oobCode: string): Promise<void>;
// @public
export interface Auth {
readonly app: FirebaseApp;
beforeAuthStateChanged(callback: (user: User | null) => void | Promise<void>): Unsubscribe;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did we want to tackle the second argument, the optional onAbort callback in this PR?

That is just a () => void that is called useful if beforeAuthStateChanged is called multiple times and a subsequent callback rejects the state change. This gives developers a chance to walk back any side effects.

Useful to me as I integrate with the web frameworks work, for authenticated server-context. onAbort would allow me the chance to destroy the just minted cookie, if the developer is using beforeAuthStateChanged too and rejected the sign in.

@hsubox76 hsubox76 changed the base branch from master to auth-middleware April 13, 2022 21:40
@hsubox76 hsubox76 changed the title WIP: Auth middleware (beforeAuthStateChanged) Auth middleware (beforeAuthStateChanged) Apr 13, 2022
@hsubox76
Copy link
Contributor Author

Merging into auth-middleware feature branch where work will continue with ongoing PRs.

@hsubox76 hsubox76 merged commit 91406c5 into auth-middleware Apr 13, 2022
@hsubox76 hsubox76 deleted the ch-auth-middle branch April 13, 2022 21:47
sam-gc pushed a commit that referenced this pull request May 4, 2022
@firebase firebase locked and limited conversation to collaborators May 14, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants