Firebase Auth - how to refresh access token without current user object - firebase

I am accessing my backend with an access token obtained from firebase auth the following way:
login via email & password
receive the current user object
obtain the token from the user object
store the token locally to allow furher access to my backend (which uses firebase admin to validate the token)
This works, as long as the access token is stale.
This may as well work, if the application remains open and an 403 due to an expired token can be catched (I can just reuse the current user object to obtain a new token). However, if the token expires while the app is closed, opening it again (no more user object) results in forcing the user to reenter its credentials, does it?
One way that came to my mind was using the custom tokens functionality:
I could send the refresh token to the client after a login, which then stores it and would use it to log in (in an automatic manner) instead of using the credentials.
But the word "custom" made me think that I am on the wrong way somehow. There surely must be an easy way to do this with the intended functions.
Can any one help me out with this?
Greetings,
Codehai

Using this listener refreshes the token automatically, won't work in editor.
For my code to work, somehow I have to add TaskScheduler.FromCurrentSynchronizationContext() on all Firebase Tasks..
void Start()
{
auth = Firebase.Auth.FirebaseAuth.DefaultInstance;
auth.IdTokenChanged += IdTokenChanged;
}
void IdTokenChanged(object sender, System.EventArgs eventArgs)
{
Firebase.Auth.FirebaseAuth senderAuth = sender as Firebase.Auth.FirebaseAuth;
if (senderAuth == auth && senderAuth.CurrentUser != null && !fetchingToken)
{
fetchingToken = true;
senderAuth.CurrentUser.TokenAsync(true).ContinueWith(
task =>
{
if (task.IsCanceled)
{
Debug.Log("canceled");
}
if (task.IsFaulted)
{
foreach (var errors in task.Exception.InnerExceptions)
{
Debug.Log(errors.InnerException.Message);
}
}
Debug.Log("New Token: " + task.Result);
// save task.Result
fetchingToken = false;
}, TaskScheduler.FromCurrentSynchronizationContext());
}
}
private void OnDestroy()
{
auth.IdTokenChanged -= IdTokenChanged;
}

Related

Xamarin.Forms get new Token when session is over

I have this scenario: Xamarin.Forms App connected with Web Api 2. I make all requests and get the data i want. Now when the session token expires, i need to refresh the token but don't logout the user. The user don't need to know when token is refreshed. How to organize this, add in every request if statement when i send it and check if token expires.
This is one of my requests:
public async Task<User> GetProfileSetup()
{
try
{
if (CrossConnectivity.Current.IsConnected)
{
string token = DependencyService.Get<ISharedFunctions>().GetAccessToken();
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Add("Accept", "application/json");
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + token);
var response = await client.GetAsync(#"api/Profile/GetProfilSetup");
if (response.IsSuccessStatusCode)
{
string jsonMessage;
using (Stream responseStream = await response.Content.ReadAsStreamAsync())
{
jsonMessage = new StreamReader(responseStream).ReadToEnd();
}
User user = JsonConvert.DeserializeObject<User>(jsonMessage);
return user;
}
else
{
var m = response.Content.ToString();
return null;
}
}
else
{
return null;
}
}
catch (Exception ex)
{
Debug.WriteLine(ex);
string error = ex.Message;
return null;
}
}
P.S I have Methods for GetToken and RefreshToken in my Api and they are working, just how to organize Refreshing ?
It really depends on what libraries are you using on your project.
But let's say you're using plain c# to handled your HTTP calls.
[OPTION 1] Polly
I can recommend you looking at Polly
It's a great library with a lot of features. You can use the Retry policy to handled expired tokens:
var _unauthorizedPolicy = Policy
.Handle<Exception>(ex => ex.StatusCode == HttpStatusCode.Unauthorized) // check here for your exception to be the right one
.RetryAsync(3, async (exception, retryCount, context) =>
{
try
{
var token = await _authService.RefreshToken();
// save the new token or whatever you need to store it
}
catch (Exception ex)
{
// RefreshToken failed, you should probably sign out the user
SignOut();
}
});
What this does is that Polly will try to execute your normal HTTP call and in case it fails and the cause is specified in Handle, then a retry mechanism is fired that will try to refresh the token and then retry your request. In the end, in case the token cannot be refreshed, you sign out the user. Of course, all this can be customized, check Polly's documentation is pretty well written.
Please note that inside Handle<T> you must put the right exception. I just used Exception as a placeholder since I'm not sure what Exception is thrown in your case.
Then you would call your method with this policy:
var result = await _unauthorizedPolicy.ExecuteAsync(() => GetProfileSetup())
And you can reuse that policy for any call, no need to create it every time.
[OPTION 2] DelegatingHandler
I will like here another StackOverflow answer:
How to Refresh a token using IHttpClientFactory
Basically you can intercept every HTTP call made via a HttpClient and refresh/add a token to your requests.
Note that that answer does not obligate you to use IHttpClientFactory, it also works for a simple HttpClient.
Also a little bit off-topic. You might want to look up for libraries to handle htt calls such as Retrofit. It will really reduce the amount of boilerplate code.

Log a user in using a generated Anonymous Login ID

I am writing an Android application and I am trying to log users in anonymously so they don't have to go through any sort of registration process. I am storing their anonymous user ID in shared preferences, and when the application opens, I am trying to log them in based on that user ID. I am trying to figure out the correct way to do this, as there doesn't seem to be an auth function that just takes in a UID. Currently I have it using auth(), but I don't feel like that is correct.
Here is some sample code:
String userID = getUserID();
if(userID.equals("NOT_FOUND")) {
ref.authAnonymously(new Firebase.AuthResultHandler() {
#Override
public void onAuthenticated(AuthData authData) {
//successful authentication
//save auth data
SharedPreferences prefs = getSharedPreferences(
"USER_ID", Context.MODE_PRIVATE);
String id = authData.getUid();
prefs.edit().putString("USER_ID", id).commit();
}
#Override
public void onAuthenticationError(FirebaseError firebaseError) {
//unsuccessful authentication
}
});
} else {
ref.auth(userID, new Firebase.AuthListener() {
...
You're creating a new authentication session each and every time you invoke FirebaseRef.authAnonymously(...). This method only needs to be invoked once, after which the user will authenticated upon page refreshes. Also note that you do not need to call FirebaseRef.auth() again once restarting the application, as that piece is automatically handled for you.
If you'd like to check for the current authentication state of the user, and only then create a new authentication session if the user is not currently authenticated, use the synchronous accessor for authentication state FirebaseRef.getAuth().
Lastly, once you create an anonymous authentication session, no new sessions may ever be created with the same uid. That session will live until your predefined session expiration time (configured in your account dashboard) or until your user logs out, after which that uid is permanently retired.

where to store user credentials in front end

in ASP.NET web API in the log in algorithm i have a action filter that generates a token for each user and the front end sends that token back to authenticate the user by using that token in web server i can get current user information till now every thing is working fine however i have new requirements that every user has relation many to many with account which means the same user can exists in more than one account with different roles for example in account one he is an admin in account two he is normal user so i have to regenerate the token which requires the user to re log in again i do not want him to be redirected to the log in page again. what i think of is to store user name and password in html 5 local storage but i think that is a bad practices any ideas.
Her is how i generate token.
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (!actionContext.Request.Headers
.Any(header => header.Key == "AuthorizationHeader"))
{
if (this.IsAnonymousAllowed(actionContext) == false)
{
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized, "Un Autherized");
}
}
else
{
string token = actionContext.Request.Headers
.Where(header => header.Key == "AuthorizationHeader")
.First().Value.First();
if (this.IsAnonymousAllowed(actionContext) == true)
{
return;
}
string passPhrase = System.Configuration.ConfigurationSettings.AppSettings["PassPhrase"];
string ticket_string = Crypto.Decrypt(token, passPhrase);
TicketData ticket = JsonConvert.DeserializeObject<TicketData>(ticket_string);
if (ticket == null || ticket.Expiration < DateTime.Now)
{
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized, "UnAuthorized");
}
else
{
OurIdentity identity = (OurIdentity)ticket.TokenData.OurIdentity;
System.Threading.Thread.CurrentPrincipal = new OurPrincipal
{
OurIdentity = identity,
};
}
}
}
You are right saving username and password in the local storage is bad. It is bad to save it anywhere on the client.
Usually a token is generated and put in a cookie. That token corresponds with a record on the server, in either a session log or a database.
I strongly suggest to use existing methods for this, like OAUTH Bearer tokens in this tutorial.
As far as I understand, if you are storing a hash (perhaps with a salt for extra protection) it is not nessecescarily bad to store the credentials. These would have to be stored somewhere at the end of the day anyway.

ASP.NET Web API how to authenticate user

I'm trying to create a simple user authentication function but I just can't get it to work.
Here is the code I'm working on:
public class LoginController : ApiController
{
private void SetPrincipal(IPrincipal principal)
{
Thread.CurrentPrincipal = principal;
if (HttpContext.Current != null)
{
HttpContext.Current.User = principal;
}
}
public bool Login(string token)
{
//Check token
if (.....)
{
//Authenticate user
var identity = new GenericIdentity("Test user");
SetPrincipal(new GenericPrincipal(identity, new string[]{"Test role"}));
}
}
[Authorize]
public string TestFun()
{
return "Hello " + User.Identity.Name;
}
}
So, if I try to call method TestFun() first, it returns error code 401 like it should.
However when I call method Login() it should somehow save user credentials, but this is where I get lost, I just can't get it to work.
TestFun() always returns error code 401 even if I call Login() first.
If I try to put return "Hello " + User.Identity.Name; in the Login() function it returns correct username, but in the TestFun() the user is not available.
I've even tried using Sessions and FormsAuthentication but I just can't get it to work, even on this really simple example.
Can someone please tell me what am I missing?
Thanks!
The Login method sets the principal for current request only. Just after the request completes, the principal context is wiped out so that the server can handle other requests for other users. When a new request comes, eons later from the server perspective, the principal context no longer exists and if nothing restores it, the request is unauthenticated.
To fix this you have to return something from your login method to the client. Not only bool but rather - an authentication token. Something the client could use to authenticate further requests.
It could be anything. Forms cookie would be fine as long as the client remembers to append it to further requests. Another common practice is to have a custom authentication token returned to the client and then appended by the client in a custom authentication header. And as forms cookies are handled by the Forms Authentication module, custom headers would need a custom mvc authentication filter or custom asp.net authentication module so that the token is readed, the identity is extracted and restored just before the request is about to execute.
If you don't like to bake your own token infrastructure, I would also recommend OAuth2 tokens. There is a great book that contains easy to follow examples on this and other possible authentication methods:
http://www.amazon.com/Pro-ASP-NET-Web-API-Security/dp/1430257822/ref=sr_1_1?ie=UTF8&sr=8-1&keywords=web+api+security
I just got the same issue, yes, I agreed we need to save that principal into somewhere (cookie, session) for other action to use, so, in SetPrincipal function I added
HttpContext.Current.Session["user"] = HttpContext.Current.User;
Now, the issue is how to get it back for other action, the idea popups in my mind is to extend AuthorizeAttribute and override IsAuthrized function, it will read the session first and if it found the session, it will return true, otherwise it will return false.
namespace BinZ
{
public class MyAuthorizeAttribute:AuthorizeAttribute
{
protected override bool IsAuthorized(HttpActionContext actionContext) {
HttpContext.Current.User = HttpContext.Current.Session["user"] as IPrincipal;
return HttpContext.Current.User != null;
}
}
}
Please remember to replace [Authorize] to [MyAuthorizeAttribute] in WebApi controller.
It works for me very well.
Cheers

Passing the username/password from client to web API using GET

for example I have a web API : http://example.com/api/product.
I have a C# client to consume this web API. Something like that to get whole list of product.
// List all products.
HttpResponseMessage response = client.GetAsync("api/products").Result; // Blocking call!
if (response.IsSuccessStatusCode)
{
// Parse the response body. Blocking!
var products = response.Content.ReadAsAsync<IEnumerable<Product>>().Result;
foreach (var p in products)
{
Console.WriteLine("{0}\t{1};\t{2}", p.Name, p.Price, p.Category);
}
}
else
{
Console.WriteLine("{0} ({1})", (int)response.StatusCode, response.ReasonPhrase);
}
How do I pass the username and password from C# client to server's API? What I want is when the C# client to get whole product list from web API.
The client will send the username and password to the server's API. if the server's web API checks whether it is authorized user from database, if not don't let it get product list.
I used the following approach in a proof of concept some time ago, I hope it helps you.
I wrote something like this, an "AuthenticationController" with 2 methods:
public bool Login(string username, string password, bool rememberMe)
{
if (Membership.ValidateUser(username, password))
{
FormsAuthentication.SetAuthCookie(username, rememberMe);
return true;
}
return false;
}
public void Logout()
{
FormsAuthentication.SignOut();
}
The Login method creates a cookie that will be sent to the client; then, in each request, you need to send it back to the server. You can use the [Authorize] attribute in your controller actions to validate allowed roles and rights.
My recommendation is to use have an authentication routine that will assign a token to the client. The client would then cache that token and pass that token in subsequent requests. The authentication routine should be via SSL to prevent sniffing on the wire and shouldn't be stored on the device at all (the token can be cached to the device).
This will give you a fair bit of control over the client. Your service is then in a position where it can preemptively deactivate the client (kill the token and force a re-auth - essentially a timemout situation). You are also in a position to protect your application on the client (if the application is compromised on the device the user credentials won't be passed around).
You could use DotNetOpenAuth to get you started along this path.
[System.Web.Mvc.AcceptVerbs(HttpVerbs.Post)]
public ActionResult LogOn(string loginIdentifier)
{
if (!Identifier.IsValid(loginIdentifier))
{
ModelState.AddModelError("loginIdentifier",
"The specified login identifier is invalid");
return View();
}
else
{
var openid = new OpenIdRelyingParty();
IAuthenticationRequest request = openid.CreateRequest(
Identifier.Parse(loginIdentifier));
// Require some additional data
request.AddExtension(new ClaimsRequest
{
BirthDate = DemandLevel.NoRequest,
Email = DemandLevel.Require,
FullName = DemandLevel.Require
});
return request.RedirectingResponse.AsActionResult();
}
}
Source: Sample Code

Resources