Skip to content
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

FR: Calling an Apple Revoke Token endpoint #9906

Closed
linkillhz opened this issue Jun 14, 2022 · 63 comments
Closed

FR: Calling an Apple Revoke Token endpoint #9906

linkillhz opened this issue Jun 14, 2022 · 63 comments
Assignees

Comments

@linkillhz
Copy link

Feature proposal

  • Firebase Component: Auth

Apple's new guidelines say,
If your app offers sign-in with Apple, you must use sign-in with the Apple REST API to revoke your user token when you delete your account.
https://developer.apple.com/news/?id=12m75xbj

Does Firebase Auth offer any functionality to assist with account deletion?

@peterfriese
Copy link
Contributor

Hi @linkillhz -

you can delete user accounts in Firebase Auth using user.delete():

if let user = Auth.auth().currentUser {
  do {
    try await user.delete()
  }
  catch {
    print(error)
  }
}

See the docs for more details.

To delete any of the user's associated data, you can use the Delete User Data Extension. This essentially is a Cloud Function that will run whenever a Firebase Auth user account is deleted (either using the call above or via the Firebase console), and can delete the user's data in Cloud Firestore, Cloud Storage, and the Realtime Database.

If you find that your data model is different from what the Extension currently supports:

  1. Please consider leaving feedback on the Firebase Extensions repo (file a feature request outlining how your data model looks like)
  2. Consider implementing your own Cloud Function based on the code of the Delete User Data Extension

As for revoking the token, @rosalyntan and @renkelvin can provide more details.

@linkillhz
Copy link
Author

Hi, @rosalyntan and @renkelvin

Firebase SDK did not call Apple's revoke token endpoint when I deleted my Firebase account.
I have a few questions.

  • What should be the flow for calling Apple's revoke token endpoint when deleting a Firebase account?
    Do I need to implement my own Cloud Function?
    Also, can I get the necessary parameters for Apple's revoke token endpoint?

  • Are there any plans to support calling Apple's revoke token endpoint in the Firebase SDK?

Regards

@CalHoll
Copy link

CalHoll commented Jun 16, 2022

+1 Apple is complaining about this being a requirement June 30th. Looks like apps wont be accepted after that date if they do not delete tokens. This is the message:

Starting June 30, 2022, apps submitted to the App Store that support account creation must also include an option to initiate account deletion.

We noticed this app may support account creation. If it does not, you may disregard this message. If it already offers account deletion or you’re working to implement it, we appreciate your efforts to follow the App Store Review Guidelines. Apps submitted after June 30 that do not comply with the account deletion requirements in guideline 5.1.1(v) will not pass review.

Learn more about the account deletion requirements. If your app offers Sign in with Apple, use the Sign in with Apple REST API to revoke user tokens.

@Paco777
Copy link

Paco777 commented Jun 17, 2022

+1 this is a real problem to be addressed before June 30.
Either implement it automatically in the SDK (would be great) or give us access to the requested parameters so we can do it ourselves.

@andrejandre
Copy link

andrejandre commented Jun 18, 2022

I don't think it makes sense to offer this with Firebase SDK. This is something the developer has to handle independently with apple auth. You can get the client secrets and token strings during login or sign up. It is decoupled from the Firebase flows.

However, apple has provided very poor documentation for how to implement this. Even if you test their API with something like postman, you will always get status code 200 even with incorrect parameters.

@CalHoll
Copy link

CalHoll commented Jun 18, 2022

I don't think it makes sense to offer this with Firebase SDK. This is something the developer has to handle independently with apple auth. You can get the client secrets and token strings during login or sign up. It is decoupled from the Firebase flows.

However, apple has provided very poor documentation for how to implement this. Even if you test their API with something like postman, you will always get status code 200 even with incorrect parameters.

Perhaps you could help explain how this could be implemented, for clarity, I'm using Firebase & Flutter, I use Firebase Auth with flutter and sign_in_with_apple to authenticate with Firebase via the method in the documentation: https://firebase.google.com/docs/auth/flutter/federated-auth

To delete my user, the documentation for firebase/flutter states to call user.delete().

This however would cause an app rejection via Apple's new policy. Is your position that users should know to create their own server outside Firebase, track secrets there after authenticating on Firebase, and then call that server to revoke tokens alongside the calls to firebase to delete the authenticated user?

If so where should that be documented and would a cloud function and Firestore be fine to use to keep track of tokens and revoke on delete?

@peterfriese
Copy link
Contributor

peterfriese commented Jun 18, 2022

Hi all -

Thanks for raising this issue - we are aware of this and are actively working on a solution. We will update this issue once we can share more details.

Documentation (and accompanying sample code) would definitely be part of the solution. This will likely be a blog post that shows how to use our solution, fast-followed by an update of our docs (https://firebase.google.com/docs/auth/ios/apple).

We would want to avoid developers having to stand up their own servers outside of Firebase - this would defeat the whole purpose of an mBaaS platform.

@andrejandre
Copy link

andrejandre commented Jun 20, 2022

To @CalHoll,

I would be delighted to know that the proposed solution being worked on by @peterfriese (and the Firebase team) could cover the upcoming requirement.

However, you can see my attempt at this issue here: https://stackoverflow.com/questions/72399534/how-to-make-apple-sign-in-revoke-token-post-request/72488104?noredirect=1#comment128373667_72488104

The general principle of my approach is that I am attempting to perform a POST request with limited understanding of the parameters at play. I have not been able to verify that my solution works when running in a debug environment. To my understanding, I did not think it would have been possible to meet the requirement within the Firebase ecosystem, but will be very pleasantly surprised to be educated otherwise.

@jooyoungho
Copy link

Apple's revoke API is called based on JWT signed by the developer's key, so I think it will be difficult for the client to implement the function. I think it's most intuitive to upload a key file from the Firebase console (like cloud messaging) and automatically process it when user.delete(). However, with Apple's instruction request date just around the corner, the way to generate JWT and invoke API via Firebase functions seems not bad.

@ewerspej
Copy link

Firebase functions require the Blaze plan, though. My app is small and so far I haven't used Firebase functions, yet. It would be great if we could just upload the JWT file to Firebase and Firebase takes care of the rest after calling delete() on the user account.

@algrid
Copy link

algrid commented Jun 20, 2022

For now I'm trying to implement the revocation calls myself, but I can't find a way to get access_token and refresh_token that I need to revoke. Does Firebase allow to get them? Maybe in some delegate calls during authentication?

@DennisAshford
Copy link

Thanks for working on this Firebase team. I hope the fix and docs come out soon, as June 30 is right around the corner and no new app updates will be accepted without this implemented.

@unity16th30317
Copy link

unity16th30317 commented Jun 22, 2022

I'm also trying to fill in the "token" parameter,
The identityToken I get when I try to log in with Firebase
But this parameter filling does not work,
Trouble Firebase tell me if there is a way to get the token parameters required for Post
And what is the parameter type refresh_token or access_token

@unity16th30317
Copy link

Hi all -

Thanks for raising this issue - we are aware of this and are actively working on a solution. We will update this issue once we can share more details.

Documentation (and accompanying sample code) would definitely be part of the solution. This will likely be a blog post that shows how to use our solution, fast-followed by an update of our docs (https://firebase.google.com/docs/auth/ios/apple).

We would want to avoid developers having to stand up their own servers outside of Firebase - this would defeat the whole purpose of an mBaaS platform.

Sadly this blog is not very helpful

@jooyoungho
Copy link

After getting the appleIDCredential.authorizationCode from the app, you need to get the refresh token through the auth/token API.

The whole process is as follows.

  1. Get authorizationCode from App where user log in.
  2. Get a refresh token with no expiry time using authorizationCode with expiry time.
  3. After saving the refresh token, revoke it when the user leaves the service.

I implemented it in Firebase functions and put the code to be reference on the link below.
https://github.com/jooyoungho/apple-token-revoke-in-firebase

If you did it right, it would have disappeared from your device's Settings - Apps using Apple Sign-in.

@unity16th30317
Copy link

unity16th30317 commented Jun 22, 2022

Thanks,it works!

After getting the appleIDCredential.authorizationCode from the app, you need to get the refresh token through the auth/token API.

The whole process is as follows.

  1. Get authorizationCode from App where user log in.
  2. Get a refresh token with no expiry time using authorizationCode with expiry time.
  3. After saving the refresh token, revoke it when the user leaves the service.

I implemented it in Firebase functions and put the code to be reference on the link below. https://github.com/jooyoungho/apple-token-revoke-in-firebase

If you did it right, it would have disappeared from your device's Settings - Apps using Apple Sign-in.

Thanks!!!!! It works!

@DennisAshford
Copy link

Is there a general timeframe for when this will be implemented in the Firebase Auth flow? Curious if it is worth putting in the time for a custom solution or just wait for Firebase to implement this.

@maor-loora
Copy link

@peterfriese Is there any expected solution within the coming days?
June 30th is getting closer and we wouldn't wanna be rejected due the Apple's requirements, and we'd rather not use some cumbersome custom solutions for that.

@mikehardy
Copy link
Contributor

From collective analysis on invertase/react-native-apple-authentication#282 it appears the only way to do the revoke is to generate a fresh valid authorization code + a fresh JWT built with a client secret that Firebase Auth has already

It is possible to pre-generate and store a JWT locally (in app) with a maximum expiry of 6 months (per documentation) at cost of possible spoofing with exposed JWT plus need to update app periodically or you have a server API somewhere (could be here!) that can take the known secret firebase auth already has to generate the JWT for post to the Apple REST API for token revocation

It does not appear there is any avoiding some user interaction though as I believe you need an authorization code from the Sign In With Apple APIs which is short-lived and based on my understanding of the Firebase Auth APIs, Firebase does not itself maintain a token to revoke?

So if I understand correctly and all the above is correct - the optimization possible here is the elimination of the need for a server by use of some possible new firebase auth API and existing firebase servers, that can generate take a non-expired apple authorization code from a caller (perhaps react-native-apple-authentication, for react-native people) and do the rest (JWT generation, REST API calls). That's definitely useful so I'm interested in a timeline too if possible.

As a library maintainer, it will take me a while to wrap anything that comes up for react-native-firebase and to communicate how to use it for react-native-apple-authentication users

@Knapiii
Copy link

Knapiii commented Jun 27, 2022

After getting the appleIDCredential.authorizationCode from the app, you need to get the refresh token through the auth/token API.

The whole process is as follows.

  1. Get authorizationCode from App where user log in.
  2. Get a refresh token with no expiry time using authorizationCode with expiry time.
  3. After saving the refresh token, revoke it when the user leaves the service.

I implemented it in Firebase functions and put the code to be reference on the link below. https://github.com/jooyoungho/apple-token-revoke-in-firebase

If you did it right, it would have disappeared from your device's Settings - Apps using Apple Sign-in.

Hey @jooyoungho.
I am able to retrieve the appleIDCredential.authorizationCode at signIn/signUp, but how do I retrieve it while the user is already signed in using firebase? The user never goes through ASAuthorizationControllerDelegate if they have already signed in since last time they used the app.

@andrejandre
Copy link

andrejandre commented Jun 27, 2022

but how do I retrieve it while the user is already signed in using firebase? The user never goes through ASAuthorizationControllerDelegate if they have already signed in since last time they used the app.

@Knapiii You can implement a user-facing 'reauthentication' flow. That way you can require reauthentication prior to displaying the account deletion UI in your application. Doing that effectively requires that the user tap on another Sign In With Apple or Continue With Apple button.

See https://firebase.google.com/docs/auth/ios/apple#reauthentication_and_account_linking for more details. Doing this safely guarantees you will have the sensitive information required to revoke the token without any of it expiring.

Keep in mind, when reading the Firebase account deletion documentation, it can be considered a 'sensitive' action which would require reauthentication. Firebase recommends performing reauthentication for 'sensitive' actions. I think account deletion and revoking the token from Sign in With Apple are considered sensitive with regards to backend services.

@Paco777
Copy link

Paco777 commented Jun 27, 2022

Please, Firebase (@peterfriese) , when you implement the solution, make it possible to revoke the Apple token of the user also from a web server, and not only from the app.

I delete the user account from a web server (using this firebase php api https://github.com/kreait/firebase-php), because It does not force the user to re-authentify before account deletion. (I already display 2 confirmations popup in the app, to ensure that the user really wants to delete his account).

@peterfriese
Copy link
Contributor

peterfriese commented Jun 27, 2022

Hi all,

We are working on a long-term solution that will call Apple's REST API for token revocation when you delete a Firebase user account that was created using Sign in with Apple.

In the meantime, we recommend choosing one of the following approaches:

  1. If your project is on the Blaze plan: implement a Cloud Function for revoking the user's access token, similar to this one.

  2. If your project is on the Spark plan (and you don't want to upgrade to Blaze), follow the steps outlined by Apple in their Apple Developer Forums post "Handling account deletions and revoking tokens for Sign in with Apple":

    • Delete the user's account data from your systems.
    • Direct the user to manually revoke access for your client.
    • Be sure to clearly communicate that all apps associated with your developer account will be revoked for their user account as well.
    • Respond to the credential revoked notification to revert the client to an unauthorized state. [Note: check the forum post for a detailed explanation of why this last step is critically important.]

Since Apple's Delete User Data requirements state that you should delete the user's data from your systems, we expect many of you will either implement their own Cloud Function for deleting the user's data from any Firebase services you use (such as Cloud Firestore, Realtime Database, or Cloud Storage), or use the Delete User Data Extension. In this case, we recommend choosing option 1 above.

As mentioned, we are actively working on a long-term solution that should make the token revocation process a smooth experience. Once we have an implementation that we can share with you, we will update this GitHub issue.


PS: please don't store any sensitive information in UserDefaults - this is not secure.

@peterfriese
Copy link
Contributor

@peterfriese. Is the solution at a point where we should just wait for the Firebase implementation in early Q1? Or implement the workaround? Just wanted to see if it was still on schedule for early Q1. Thx!! PS - Need any guinea pigs? :)

Yes, we're still on track for early Q1. We'll reach out to this thread once we've got something that you can take for a spin.

@phoheisel
Copy link

I stuck with my iOS App Version because of this issue. Anyone got a interim solution with the JS SDK running?

https://github.com/jooyoungho/apple-token-revoke-in-firebase is the best interim solution according to me. I successfully published my iOS app on the App Store with this solution after getting a rejection the first time.

Doesn't realy help with the JS SDK as I don't understand how to get the mentioned authorizationCode. I also created a Q&A Post here to get help: #10565

Do I understand it right that the early Q1 solution will not depend on any SDK but directly revoke Apple ID Logins when deleteing a firebase User connected to Apple ID?

@peterfriese
Copy link
Contributor

Do I understand it right that the early Q1 solution will not depend on any SDK but directly revoke Apple ID Logins when deleteing a firebase User connected to Apple ID?

To revoke a token, you will have to call the revokeToken method, similar to this:

let appleIDCredential = authorization.credential
let auth = Auth.auth()
do {
  try await auth.revokeToken(appleIDCredential.authorizationCode)
} catch {
    print("Token revocation failed with error \(error)")
}

We considered making this a back-end only implementation, but this would require the Sign in with Apple access/refresh tokens/authorization code to be stored in our backend. We decided against this for a number of reasons:

  1. Storing the tokens in our backend increases the risk of them being stolen in the (unlikely) event of our backend being compromised. In the interest of security, we decided to err on the side of being more secure.
  2. Since deleting the user account is a security-sensitive operation, you need to re-authenticate the user anyway (see the Firebase Authentication documentation). So even if we did store the token in the backend, you would have to re-auth the user to get a fresh token.

You can follow #10580 for the implementation of the token revocation API.

@ciriousjoker
Copy link

@peterfriese

The revokeToken() could fail, so I assume it's meant to be called first and if it works successfully, then we delete the user's account?

@the-dut
Copy link

the-dut commented Dec 15, 2022

To revoke a token, you will have to call the revokeToken method, similar to this:

let appleIDCredential = authorization.credential
let auth = Auth.auth()
do {
  try await auth.revokeToken(appleIDCredential.authorizationCode)
} catch {
    print("Token revocation failed with error \(error)")
}

So it sounds like the Q1 solution will be something like:

  • Have the user re-login to get the Apple credential/auth code.
  • Call the revokeToken method on the Firebase auth object
  • Magic happens.

If it's that easy.. and all done in my UI.. I'm definitely waiting until Q1!
Thanks!!!

@CristianMoisei
Copy link

Hey guys, have there been any more updates on this?

@dallasryk
Copy link

ditto

@znromonk
Copy link

znromonk commented Feb 7, 2023

If anyone is using Flutter and hung up on this, here is a temporary workaround.

Delete user (App side)

Since deletion is "security-sensitive operation", asking the user to log in again is recommended. I tried the Firebase Auth for providing the token and it did not work. I had to use the_apple_sign_in for that.

Flutter Code
deleteUser() async {
  final AuthorizationResult res =
      await TheAppleSignIn.performRequests([
    const AppleIdRequest(
        requestedScopes: [Scope.email, Scope.fullName])
  ]);

  switch (res.status) {
    case AuthorizationStatus.authorized:
      try {
        // Get Token
        final AppleIdCredential appleIdCredential = res.credential!;
  
        String accessToken = String.fromCharCodes(
            appleIdCredential.authorizationCode!);
  
        try {
          HttpsCallableResult resp = await FirebaseFunctions.instance
              .httpsCallable('revokeToken')
              .call(accessToken);
          debugPrint(resp.data.toString());
  
          if (resp.data["status"] != null) {
            if (resp.data["status"].toString() == "success") {
              await FirebaseAuth.instance.currentUser!.delete();
              await signOut();
            }
          }
        } catch (firebaseFunctionError) {
          debugPrint(firebaseFunctionError.toString());
          return firebaseFunctionError.toString();
        }
      } on PlatformException catch (error) {
        debugPrint(error.message);
        return error.code;
      } on FirebaseAuthException catch (error) {
        debugPrint(error.message);
        return error.code;
      }
    case AuthorizationStatus.error:
      return 'auth-error';
    case AuthorizationStatus.cancelled:
      return 'auth-cancelled';
    default:
      return 'auth-error-other';
  }
}

Revoke function (Server side)

To revoke, use the authorization code obtained in the app to generate a request_token. Use this request_token to perform the revocation. The makeJWT() and the original code I modified for this was provided by @jooyoungho #9906 (comment)

Replace com.example.app in both instances of "client_id": "com.example.app", with your bundleID.

Firebase Function
exports.revokeToken = functions.https.onCall(async (request, response) => {
  // import the module to use
  const axios = require("axios");
  const qs = require("qs");

  const authorizationCode = request.toString();
  const clientSecret = makeJWT();

  let data = {
    "code": authorizationCode,
    "client_id": "com.example.app",
    "client_secret": clientSecret,
    "grant_type": "authorization_code",
  };

  // first get the refresh token
  try {
    const res = await axios.post("https://appleid.apple.com/auth/token", qs.stringify(data), {
      headers: {
        "Content-Type": "application/x-www-form-urlencoded",
      },
    });

    const refreshToken = res.data.refresh_token;

    data = {
      "token": refreshToken,
      "client_id": "com.example.app",
      "client_secret": clientSecret,
      "token_type_hint": "refresh_token",
    };

    // use the refresh token to revoke
    try {
      const res = await axios.post("https://appleid.apple.com/auth/revoke", qs.stringify(data), {
        headers: {
          "Content-Type": "application/x-www-form-urlencoded",
        }});

      functions.logger.log(res.status);
      return {"status": "success"};
    } catch (revokeError) {
      functions.logger.log(revokeError);
      return {"status": "failed", "message": revokeError.toString()};
    }
  } catch (refreshError) {
    functions.logger.log(refreshError);
    return {"status": "failed", "message": refreshError.toString()};
  }
});

Note!

This is just a proof-of-concept code. Do the appropriate error checks and take security measures as needed.

@jesus-mg-ios
Copy link

Is it still in progress?

@peterfriese
Copy link
Contributor

Is it still in progress?

Yes, it is.

@harryFBloch
Copy link

spent the last 2 days trying to get the cloud functions working just keep getting a 400 code from apple. Really hope this is fixed soon.

@yuriibabii
Copy link

For those who are running into 400 error:

AuthorizationCode leaves only 5 minutes, and can be used only once. Otherwise, I'm getting 400 error. I haven't found this information anywhere except apple official documentation
https://developer.apple.com/documentation/sign_in_with_apple/generate_and_validate_tokens

@TheoBo
Copy link

TheoBo commented Apr 20, 2023

We are still looking for a practical solution to this problem.

@peterfriese
Copy link
Contributor

This will be launching in the next release of the Firebase SDK.

@morganchen12 morganchen12 added this to the 10.9.0 - M131 milestone Apr 20, 2023
@paulb777
Copy link
Member

Actually, my understanding is that this was fixed in #11001 and already released in 10.8.0.

cc: @renkelvin

@paulb777 paulb777 removed this from the 10.9.0 - M131 milestone Apr 20, 2023
@CristianMoisei
Copy link

@paulb777 Just to confirm, is the expected behaviour that calling Auth.auth().currentUser.delete { .. } should revoke the token automatically also? I tested this now so using 10.8.1, called delete but the token still appears in the apple id page. Could it be because the app isn't live on the app store?

@peterfriese
Copy link
Contributor

peterfriese commented Apr 20, 2023

Note: [UPDATE]
The documentation has been updated. In particular, see steps 2 and 3 in Configure Sign In with Apple, as well as the Token Revocations section

Hi @CristianMoisei - token revocation needs to triggered separately from deleting the user account.

We're currently working on updating the docs (and a video is also on the way), but broadly speaking, you will have to do the following:

  1. Make sure you filled out the Services ID and OAuth code flow configuration
    section of the Sign in with Apple provider configuration in the Firebase console

  2. Since Firebase does not store user tokens when users are created with Sign in with Apple, you must ask the user to sign in again before revoking their token and deleting the account. To avoid confusing users, you should show a dialog first, telling the user that they need to authorize deleting their account. For an example of how to do this, have a look at the Craft app

  3. Obtain the authorization code from the ASAuthorizationAppleIDCredential, and use it to call Auth.auth().revokeToken(withAuthorizationCode:) to revoke the user's tokens.

  4. Finally, delete the user account (and all associated data)

This works independently of whether your app is in the App Store or not.

@TheoBo
Copy link

TheoBo commented Apr 21, 2023

I try to understand Peter's recommendations, but do not understand how to get ASAuthorizationAppleIDCredential. We use flutter with firebase_auth.

@peterfriese
Copy link
Contributor

I try to understand Peter's recommendations, but do not understand how to get ASAuthorizationAppleIDCredential. We use flutter with firebase_auth.

I've filed a feature request on the FlutterFire repo for you, @TheoBo

@teeeeeegz
Copy link

Thank you @peterfriese, works great!

I was wondering what would be best practice for handling token revocation while using the local Firebase emulator?

When attempting to revoke the token with the Auth.auth().revokeToken(withAuthorizationCode: authCode) function, FIRAuthErrorDomain 17004 error is thrown which refers to "ERROR_INVALID_CREDENTIAL".

Would simply skipping the above function be the way to go if the local emulator is being used?

Thanks!

@igorzhukov
Copy link

igorzhukov commented May 8, 2023

@peterfriese sorry, but Auth.auth().revokeToken(withAuthorizationCode:) method is absent in the latest master branch.

I imported FirebaseCore, FirebaseAuth

@peterfriese
Copy link
Contributor

@peterfriese sorry, but Auth.auth().revokeToken(withAuthorizationCode:) method is absent in the latest master branch.

I imported FirebaseCore, FirebaseAuth

It should be there: https://github.com/firebase/firebase-ios-sdk/blob/8efafcb340eb097f4faa2a3c24e46185ff28b28e/FirebaseAuth/Sources/Public/FirebaseAuth/FIRAuth.h#LL858C31-L858C31 - can you double check you're either on master or on 10.8.0 or higher?

@igorzhukov
Copy link

igorzhukov commented May 8, 2023

@peterfriese sorry, but Auth.auth().revokeToken(withAuthorizationCode:) method is absent in the latest master branch.
I imported FirebaseCore, FirebaseAuth

It should be there: https://github.com/firebase/firebase-ios-sdk/blob/8efafcb340eb097f4faa2a3c24e46185ff28b28e/FirebaseAuth/Sources/Public/FirebaseAuth/FIRAuth.h#LL858C31-L858C31 - can you double check you're either on master or on 10.8.0 or higher?

thank you, it's on the master for spm.

did you update Cocoapods btw?
which version/branch should I use for pods Firebase and FirebaseAuth?

@paulb777
Copy link
Member

paulb777 commented May 8, 2023

CocoaPods and SPM use the same versioning. We release them at the same time.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.