Coder Social home page Coder Social logo

Comments (14)

bernhardgessler avatar bernhardgessler commented on May 29, 2024 1

Are you sure you'd need to refresh the third party tokens as well? From what I understand from the docs and after playing with custom tokens is that any third party token is just used once initially, to receive a firebase token. That one can then be refreshed calling:

https://securetoken.googleapis.com/v1/token?key={apiKey}

Form data:
grant_type:refresh_token
refresh_token:{refreshToken}

Response:

{
  "access_token": "{seems to be identical with the idToken}",
  "expires_in": "3600",
  "token_type": "Bearer",
  "refresh_token": "{refreshToken. Seems to the the same as we passed in}",
  "id_token": "{newToken}",
  "user_id": "{UID}",
  "project_id": "{projectId}"
}

So shouldn't it be enough to check before any query/insert if the token is about to expire. And if so, do a refresh first, then go on doing the query/insert?

from firebase-authentication-dotnet.

bezysoftware avatar bezysoftware commented on May 29, 2024 1

If you manage to implement it you can do a PR and I'll merge it into this lib ;)

from firebase-authentication-dotnet.

bezysoftware avatar bezysoftware commented on May 29, 2024

You need to call SignIn method again, refreshing is not supported. This is due to multiple possible sources of the underlying oauth login data (facebook / google / etc.) and the lib would need to handle refreshing that as well, which would required a lot more work.

from firebase-authentication-dotnet.

sessio avatar sessio commented on May 29, 2024

Ok, at the moment i implemented this by calling SignIn automatically before ExpiresIn happens, but this is far from ideal. @bernhardgessler 's idea sounds good to test out in a fork if i have the time.

from firebase-authentication-dotnet.

sessio avatar sessio commented on May 29, 2024

Swamped with other projects atm, but let's see in the near future :)

from firebase-authentication-dotnet.

itswisdomagain avatar itswisdomagain commented on May 29, 2024

Calling https://securetoken.googleapis.com/v1/token?key={apiKey} with Firebase API Key produces a 400 Bad Request error:

{
"error": {
"code": 400,
"message": "API key not valid. Please pass a valid API key.",
"status": "INVALID_ARGUMENT",
"details": [
{
"@type": "type.googleapis.com/google.rpc.Help",
"links": [
{
"description": "Google developers console",
"url": "https://console.developers.google.com"
}
]
}
]
}
}

from firebase-authentication-dotnet.

bernhardgessler avatar bernhardgessler commented on May 29, 2024

You need to pass some info in the body. See here
Basically, the folllowing code (in the Auth Provider) is working:

public async void RefreshAuth(FirebaseAuthLink auth)
{
    var GoogleRefreshAuth = "https://securetoken.googleapis.com/v1/token?key={0}";
    var content = $"{{\"grant_type\":\"refresh_token\", \"refresh_token\":\"{auth.RefreshToken}\"}}";
    var response = await this.client.PostAsync(new Uri(string.Format(GoogleRefreshAuth, this.authConfig.ApiKey)), new StringContent(content, Encoding.UTF8, "application/json"));

    string responseData = await response.Content.ReadAsStringAsync();
    FirebaseAuthLink authNew = JsonConvert.DeserializeObject<FirebaseAuthLink>(responseData);
            
}

But the response names things differently. expires_in instead of expiresIn etc. So I'm still thinking how to solve this most elegantly. Also I need to find a way to automate it. .WithAuth only takes the token string. So it won't know if the token is still valid. Either we'd change that to requiring the full FirebaseAuthLink or I'm thinking about remodeling FirebaseAuth, making FirebaseToken a real property that checks itself for expiration. When created initially by the AuthProvider, FirebaseAuth could store it's creation date and a reference to the IFirebaseAuthProvider. Then once it detects that its expiring, it could contact that AuthProvider again to renew the token and then update itself.

Thoughts @itswisdomagain @sessio @bezysoftware ?
I'll try implementing this tonight. I can technically make it work, no problem. Just not sure if this is elegant... Also @bezysoftware mentioned somewhere else, that it's planned to restructure Authenticating to get rid of .WithAuth() if I remember that correctly...

from firebase-authentication-dotnet.

bezysoftware avatar bezysoftware commented on May 29, 2024

Correct, as of v2.0.1 the "WithAuth" method is no longer accessible. Instead you need to specify an Action<Task<string>> which asynchronously returns this token string. https://github.com/step-up-labs/firebase-database-dotnet#authentication

This way the process of checking the token validity can be delegated to this library easily.

However I am curious - lets say I login with an OAuth token (e.g. from facebook) and I get a firebase token. What happens if the underlying OAuth token expires? Can I still refresh the Firebase token only (without refreshing the facebook token myself)? Does the google service handle that?

from firebase-authentication-dotnet.

bernhardgessler avatar bernhardgessler commented on May 29, 2024

Didn't notice that you already pushed the update. At least I remembered correctly ;)
From what I understand about Firebase Authentication and what I pieced together e.g. from threads like this one, I keep hearing about being 'logged in forever'. This only makes sense if the token can be renewed without consulting the third party again.

I suppose the third party token is used initially for being sure about who a user is. Then the uniform firebase token is issued, to make different providers transparent. Otherwise there would be no good reason either why to change a custom token for another token.

Observing their official JS client for a couple of hours, this is what happens. It calls the above servivce every time the token is older than 3600 sec.

Looking at your new approach with the factory the issue I see there is that this code is executed every time I query Firebase. You propose to authenticate inside that method. But once I'm authenticated I don't want to authenticate again. Imagine I'm calling my own auth server which lets say takes 5 seconds. So on every query I'll spend 5 seconds waiting for being authenticated, unless I actually authenticate before, store the FirebaseAuth and inside that method only pass my token / renew my token. But for doing so, I'm just as far as I was before...

How about the following:

  1. Client construktor accepts FireBaseAuth as param:
var authProvider = new FirebaseAuthProvider(new FirebaseConfig(FirebaseApiKey));
var facebookAccessToken = "<login with facebook and get oauth access token>";
var auth = await authProvider.SignInWithOAuthAsync(FirebaseAuthType.Facebook, facebookAccessToken);

var firebaseClient = new FirebaseClient(
  "<URL>",
  new FirebaseOptions
  {
    Auth= auth 
  });
...

Internally, where currently the factory is called, you just get the token from the FirebaseAuth object:

public async Task<string> BuildUrlAsync()
{
    // if token factory is present on the parent then use it to generate auth token
    if (this.Client.Options.Auth != null)
    {
        var token = this.Client.Options.FirebaseToken;
        return this.WithAuth(token).BuildUrl(null);
    }

    return this.BuildUrl(null);
}

The actual checking happens inside the Auth object:

public class FirebaseAuth
{
    private IFirebaseAuthProvider _authProvider;
    private DateTime _lastRenewal;
    private string _firebaseToken;

    [JsonProperty("idToken")]
    public string FirebaseToken
    {  
        get
        {
            if(DateTime.Now >= DateTime.Now.AddSeconds(this.ExpiresIn)
            {
                // Expired contact Authprovider to renew
                this._authProvider.RenewToken(this);
            }

            return this._firebaseToken;
        }
        set
        {
            this._firebaseToken = value;
        }
    }

    [JsonProperty("refreshToken")]
    public string RefreshToken {  get;  set;  }

    [JsonProperty("expiresIn")]
    public int ExpiresIn {  get;  set;  }

    public User User {  get;  set;  }
}

Last step is to add RenewToken(FirebaseAuth currentAuth) inside the AuthProvider. Similar to the proposal in my last message. Just actually updating the auth object.

I think this would be a cleaner approach. What do you think?

from firebase-authentication-dotnet.

bernhardgessler avatar bernhardgessler commented on May 29, 2024

This would of course work with your current implementation as well. Just a bit extra code actually:

var authProvider = new FirebaseAuthProvider(new FirebaseConfig(FirebaseApiKey));
var facebookAccessToken = "<login with facebook and get oauth access token>";
var auth = await authProvider.SignInWithOAuthAsync(FirebaseAuthType.Facebook, facebookAccessToken);

var firebaseClient = new FirebaseClient(
  "<URL>",
  new FirebaseOptions
  {
    AuthTokenAsyncFactory = () => Task.FromResult(auth.FirebaseToken) 
  });

So I guess the key part would be the implementation within the Auth object and the Auth provider...

from firebase-authentication-dotnet.

itswisdomagain avatar itswisdomagain commented on May 29, 2024

@bezysoftware,
" lets say I login with an OAuth token (e.g. from facebook) and I get a firebase token. What happens if the underlying OAuth token expires? Can I still refresh the Firebase token only (without refreshing the facebook token myself)?"
Yes. You only need the refreshToken from Firebase to refresh a the Firebase token.

"you need to specify an Action<Task> which asynchronously returns this token string. This way the process of checking the token validity can be delegated to this library easily."
True. Here's a working code I've been using, yeah, the code needs some help, but it works as it's supposed to, for now. I'll clean it up later.

A method to check the current token's validity and refresh it if necessary.
`
if (FirebaseUser == null)
return null;

        if (FirebaseUser.TokenExpiry <= DateTime.Now)
        {
            //refresh token
            DoRefreshToken();
        }

        return FirebaseUser.FirebaseToken;

`

FirebaseUser is a class I created similar to FirebaseAuthLink (I just didn't like the name)
`
public class FirebaseUser
{
public DateTime TokenExpiry { get; private set; }
public string FirebaseToken { get; private set; }
public string FirebaseRefreshToken { get; private set; }
public string FirebaseUID { get; private set; }

public FirebaseUser(Firebase.Auth.FirebaseAuthLink user)
{
    this.FirebaseUID = user.User.LocalId;
    UpdateToken(user.FirebaseToken, user.RefreshToken, user.ExpiresIn);
}

public FirebaseUser(string token, int secondsExpiry, string refreshToken, string uid)
{
    this.FirebaseUID = uid;
    UpdateToken(token, refreshToken, secondsExpiry);
}

public void UpdateToken(string token, string refreshToken, int secondsExpiry)
{
    this.FirebaseToken = token;
    this.TokenExpiry = DateTime.Now.AddSeconds(secondsExpiry - 300); //5 minutes difference just to be safe
    this.FirebaseRefreshToken = refreshToken;

    App.SaveFirebaseUserDetailsSecurely(this); // I do this to enable me refresh the token every the time the app opens and keep the user signed in
}

}
`

This is where the real deal is anyways: DoRefreshToken
`
public static void DoRefreshToken()
{
var client = new RestClient("https://securetoken.googleapis.com");
var request = new RestRequest("/v1/token?key=" + GoogleAuthAPI + "&grant_type=refresh_token&refresh_token=" +
FirebaseUser.FirebaseRefreshToken, Method.POST);

var response = client.Execute(request);
var auth = new
{
    access_token = "",
    expires_in = 0,
    token_type = "",
    refresh_token = ""
};
auth = JsonConvert.DeserializeAnonymousType(response.Content, auth);

FirebaseUser.UpdateToken(auth.access_token, auth.refresh_token, auth.expires_in);

}
`

Putting it all together:
Func<Task> FirebaseUserTokenGenerator = () => Task.FromResult(GetUserToken());

There. Works fluidly.
Please let me know if any questions or concerns.

from firebase-authentication-dotnet.

itswisdomagain avatar itswisdomagain commented on May 29, 2024

I do agree with @bernhardgessler, we could do this to make everything appear seamless:

Add a Func<Task> to Firebase.Auth.FirebaseAuth

All token refresh processes can now be included in the FirebaseAuth class.

Use the Func<Task> in the FirebaseAuth object as the AuthTokenAsyncFactory for the FirebaseClient constructor where necessary.

I could do a PR. Does that sound cool?

from firebase-authentication-dotnet.

bezysoftware avatar bezysoftware commented on May 29, 2024

I created a PR #14 for refreshing the token. The token refreshing code comes from @bernhardgessler but usage is slightly different - see the comment on the PR.
Let me know your thoughts, I think it should be usable and easy to integrate with FirebaseOptions, you would always just call GetFreshAuthAsync to get the latest token. Saving the token & loading it from storage should be simple as well.

from firebase-authentication-dotnet.

bezysoftware avatar bezysoftware commented on May 29, 2024

Merged and published to nuget

from firebase-authentication-dotnet.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.