We have a OAuth API which provides expiring tokens to authenticate REST APIs in our application.
What I am trying to achieve
While application is running on server when first request comes through, get the expiring token, expiry date from OAuth API and store in the application somewhere and use that token until that expiry date and request for another token after that.This Token should be used Globally across the application until it expires.
What I have Done
Setup a Method which will get token from Oauth API and writing it to web.config file as App settings with the expiry date. whenever a request comes through to hit the REST API it will check if the token is available and not expired from web.config and return the Token. if the Token is not available or expired it will get a new token from OAuth API.
Web.Config
<appSettings>
<add key="Token" value="" />
<add key="ExpiryDate" value="" />
</appSettings>
CS file
public RESTAPI GetData()
{
string Token = GetToken();
//use this Token to Authenticate REST API
}
public string GetToken()
{
string Token = ConfigurationManager.AppSettings["Token"];
DateTime ExpiryDate = DateTime.parse(ConfigurationManager.AppSettings["ExpiryDate"]);
if(Token == "" || ExpiryDate<=DateTime.now)
{
RefreshToken();
}
return ConfigurationManager.AppSettings["Token"];
}
public void RefreshToken()
{
//Consider OauthObject as object returned from Oauth API with Token and expiry date
ConfigurationManager.AppSettings["Token"] = OauthObject.Token;
ConfigurationManager.AppSettings["ExpiryDate"] = OauthObject.ExpiryDate.toString();
}
I want one token to be distributed across the application for all users until it is expired. does it work this way if i want to do that? or any other suggestions please.
Note: ASP.Net web application written in C#.
For .Net Core you can store the authentication token in memory using the IMemoryCache interface, which you can inject easily to the consumer service using the build-in DI container.
That way you can store it within memory only for the expired time span that correlated with Expires_in, so afterwards it will be deleted automatically.
class RestConsumerService
{
private readonly IMemoryCache _memoryCache;
RestConsumerService(IMemoryCache memoryCache)
{
_memoryCache = memoryCache
}
string GetOAuthToken()
{
if (_memoryCache.TryGetValue<string>("MyTokenKey", out string access_token)) //You can store the T object as well.
{
return access_token;
}
//Do Api OAuth call
//...
OAuthRespone response = fetchAccessToken();
_memoryCache.Set("MyTokenKey", response.Access_token, new TimeSpan(0, 0, result.Expires_in));
return response.Access_token;
}
}
I'm not really sure why you want to store the access token in the first place. Why not just keep the token in memory while its valid?
From what you wrote, it looks like you want your application to act on its own behalf for any user. In that case, the OAuth2 client credentials flow is your best option.
Also, it may be better to just use the token until the API returns an HTTP 401 error and then renew the token and retry the API. If you store the expiration time, you have to account for clock-skew between machines. The issued token may be encrypted, in which case you cannot get the expiration time.
Related
I am working with the Identity Server 4 sample code. In particular, for the client I am using the sample MVC Client with the Hybrid flow: https://github.com/IdentityServer/IdentityServer4/tree/master/samples/Clients/src/MvcHybrid
And for the server I am using Identity Server with in-memory clients (no Entity Framework, and no ASP.Net Identity): https://github.com/IdentityServer/IdentityServer4/tree/master/samples/Quickstarts
Both client and server have pretty much vanilla, out-of-the-box configuration.
I am trying to understand how refresh tokens expire and how a native app can pro-actively determine the expiration time (before it gets rejected by an API). My understanding is that the default expiration for refresh tokens is long:
http://docs.identityserver.io/en/latest/topics/refresh_tokens.html:
Maximum lifetime of a refresh token in seconds. Defaults to 2592000 seconds / 30 days
However, when the sample code requests a refresh token, I do not get the expected expiration time. Here is the sample code:
var disco = await _discoveryCache.GetAsync();
if (disco.IsError) throw new Exception(disco.Error);
var rt = await HttpContext.GetTokenAsync("refresh_token");
var tokenClient = _httpClientFactory.CreateClient();
var tokenResult = await tokenClient.RequestRefreshTokenAsync(new RefreshTokenRequest
{
Address = disco.TokenEndpoint,
ClientId = "mvc.hybrid",
ClientSecret = "secret",
RefreshToken = rt
});
tokenResult.ExpiresIn is 3600 seconds, which is actually the expiration of an access token. I was expecting that to be 2592000 seconds. So question #1 is: Why is this the case?
But more importantly, I know that the expiration for the refresh token is in fact the default 30 days when I use SQL Server as the data store. There is a table PersistedGrants that contains the refresh tokens, and the expiration is clearly 30 days from the issue date. So question #2 is: How can an app programmatically determine the expiration date of the refresh token it received?
I've tried to parse the RefreshToken itself, but it is not really a full JWT, so this throws an error:
var jwt = new JwtSecurityTokenHandler().ReadJwtToken(accessTokenResponse.RefreshToken);
var diff = jwt.ValidTo - jwt.ValidFrom;
I've also searched through the IdentityServer4 unit / integration tests and cannot find an example of introspecting a refresh token.
Presumably that information either needs to be somewhere in the initial token response, or there needs to be an endpoint built into Identity Server. But I can't find either of these things.
Ok, so the answer is that there is no data in the access_token response that indicates the expiration time of the refresh_token. Additionally, there is no endpoint that can be used to check the expiration.
The OAuth spec does not say anything about this, so I did not want to alter the access_token response. I wound up making my own endpoint that returns the expiration time if needed. Here is my controller action, if anyone needs a starting point:
private readonly IRefreshTokenStore _refreshTokenStore; // inject this into your controller
...
[Route("[controller]/GetRefreshTokenExpiration")]
[Authorize(...YOUR SCOPE...)]
public async Task<IActionResult> GetRefreshTokenExpiration(string refreshTokenKey)
{
var refreshToken = await this._refreshTokenStore.GetRefreshTokenAsync(refreshTokenKey);
if (refreshToken == null)
{
return NotFound(new { message = "Refresh token not found" });
}
return Ok(new {
message = "Refresh token found",
lifetime_seconds = refreshToken.Lifetime
});
}
When one calls ../token
We get access_token, expires_in, refresh_expires_in, refresh_token and other stuff
Decode access_token to get ValidTo substract expires_in from ValidTo and then add refresh_expires_in to ValidTo and that should give you the expiry date of the refresh_token.
I have implemented ASP.NET Identity authentication and OAuth authorization according to this tutorial: http://bitoftech.net/2014/06/01/token-based-authentication-asp-net-web-api-2-owin-asp-net-identity/
It's currently working but i don't fully understand where the TOKEN and it's timer is stored.
This is the code that generates token:
public class SimpleAuthorizationServerProvider : OAuthAuthorizationServerProvider
{
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
using (AuthRepository _repo = new AuthRepository())
{
IdentityUser user = await _repo.FindUser(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
}
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim("sub", context.UserName));
identity.AddClaim(new Claim("role", "user"));
context.Validated(identity);
}
}
I would guess that token is stored in the ASP.NET Identity DB or within the hosted WEB API Application, but i don't fully understand.
the token is only generated once by the provider and it is not stored anywhere. It contains the information the application needs to authenticate the request and nothing more.
Assuming you use the Json Web Tokens or JWT, then the token is nothing more than a Json object with some properties, such as when it expires, the actual duration in seconds etc.
The token last for a configurable duration, so assuming you want to reuse that token for multiple calls then the client application will need to store somewhere in a safe manner. It could be in session for example, you could store the whole token and when you need it simply check if it's still active by looking at the duration. If it's not active anymore, you either refresh the current one you have or simply request another.
You could encapsulate all this nicely with something like this :
private TokenModel GetToken()
{
TokenModel result = null;
if (this._systemState.HasValidToken(this._currentDateTime) )
{
result = this._systemState.RetrieveUserData().TokenData;
}
else
{
try
{
result = this._portalApiWrapperBase.RequestAccessTokenData();
}
catch(Exception ex)
{
this.LastErrorMessage = ex.Message;
}
finally
{
this._systemState.AddTokenData(result);
}
}
return result;
}
In my case all this user data is stored in Session and simply retrieved from there.
I am not showing all the code here, but I basically have a state provider where I store the token once I receive it the first time. The next time I need it, if it's still valid, I return it, or go and request another if it's not. All this is hidden from the app, you just call the GetToken method and it deals with everything else.
Now, the token is supposed to be application level, it's generated based on a ClientID and CLientSecret so you could easily request another one when you need to.
The token isn't stored. The user requesting the token needs to be able to pass the token on every request in order to make an authenticated call. So it's the responsibility of the client to store the token in order to do that. (that might be in-memory for short lived sessions or on disk/in a database for longer lived sessions.
There is no need for the server to store the token, since it is passed by the client on each request. One might store it in a db themselves on the server and check if the token is there. Using that kind of mechanism allows you to revoke a token by removing it from the db. There are other ways to do that though.
By timer I guess you mean the lifetime of the token. That is checked by the framework on every request. So there is no actual timer.
I have a 3 tier application structure. There is a cordova js application for end-users, an implementation of identityserver3 which serves as the OpenID authority, and an MVC app which will be access through an in-app browser in the cordova application.
The starting entry point for users is the cordova app. They login there via an in-app browser and can then access application features or click a link to open the in-app browser and visit the MVC app.
Our strategy for securing the MVC website was to use bearer token authentication, since we already logged in once from the app and didn't want to prompt the user to login again when they were directed to the MVC app:
app.Map("/account", account =>
{
account.UseIdentityServerBearerTokenAuthentication(new IdentityServer3.AccessTokenValidation.IdentityServerBearerTokenAuthenticationOptions()
{
Authority = "https://localhost:44333/core",
RequiredScopes = new string[] { "scope" },
DelayLoadMetadata = true,
TokenProvider = new QueryStringOAuthBearerProvider(),
ValidationMode = ValidationMode.ValidationEndpoint,
});
}
Since persisting the access_token on the query string is painful, I implemented a custom OAuthBearerAuthenticationProvider:
public class QueryStringOAuthBearerProvider : OAuthBearerAuthenticationProvider
{
private static ILog logger = LogManager.GetLogger(typeof(QueryStringOAuthBearerProvider));
public override Task RequestToken(OAuthRequestTokenContext context)
{
logger.Debug($"Searching for query-string bearer token for authorization on request {context.Request.Path}");
string value = GetAccessTokenFromQueryString(context.Request);
if (!string.IsNullOrEmpty(value))
{
context.Token = value;
//Save the token as a cookie so the URLs doesn't need to continue passing the access_token
SaveAccessTokenToCookie(context.Request, context.Response, value);
}
else
{
//Check for the cookie
value = GetAccessTokenFromCookie(context.Request);
if (!string.IsNullOrEmpty(value))
{
context.Token = value;
}
}
return Task.FromResult<object>(null);
}
[cookie access methods not very interesting]
}
This works, and allows the MVC application to not have to persist the access token into every request, but storing the access token as just a generic cookie seems wrong.
What I'd really like to do instead is use the access token to work with the OpenID endpoint and issue a forms-auth style cookie, which responds to logout. I found that I can add account.UseOpenIdConnectAuthentication(..) but if I authenticate via access_token, the OpenIdConnectAuthentication bits are simply skipped. Any ideas?
You don't -- access tokens are designed to be used to call web apis. You use the id_token from OIDC to authenticate the user and from the claims inside you issue your local authentication cookie. The Microsoft OpenIdConnect authentication middleware will do most of this heavy lifting for you.
I'm developing an app for iOS and Android that uses Facebook to login. The users will login on their phones using their Facebook credentials and I use Facebook's GraphAPI to authenticate them. I do not want to store their email/password unless I absolutely have to.
After authentication, I can get a myriad of information from Facebook but the one that's of most interest to my question is the access token's authorization token.
Since my app has a server side component, I also need to validate that this access token is valid on the server side (so given the access token and the Facebook user id, i should be able to validate this client), otherwise the entirety of using Facebook to authenticate users is pointless as I would need to also store username/password of the users myself.
My thought was to send the userId and the access token via SSL to my server and then use a library to validate that these tokens are valid and the user is indeed who it says it is in order to proceed with DB access and everything else server related.
I am however having a hard time finding a library in .NET that does not use ASP.NET.
Is there any library out there that can do this simple validation (given an authorization token and a user id, tell me if the user is logged in to Facebook and if so, how long the token is valid for) that does not need to inject 20 different DLLs and does not rely on ASP.NET?
I've had a look at DotNetOpenAuth but (1) it seems to need quite a few DLLs to operate which is kind of fine on its own although not ideal and (2) it seems to rely on ASP.NET and microsoft libraries that I would strongly like to avoid.
I'm running my server on Mono and would ideally like to avoid doing anything with ASP.NET since they have proven to be very unstable in the past.
Many thanks,
You might want to try a service like https://oauth.io/home which handles that oAUth stuff for you. According to the docs once you set it up you can simply use rest to make authenticated calls. http://docs.oauth.io/#simple-server-side-authorization
Ok I found an easy way to do it.
First, I downloaded their .Net library from NuGet:
<package id="Facebook" version="7.0.6" targetFramework="net40" />
Then here's the process in order to authenticate users.
Step 1
Get the server's Access Token (this has to be done once at the startup of the service)
var client = new FacebookClient
{
AppId = appId, // get this from developer.facebook
AppSecret = appSecret, // get this from developer.facebook
};
dynamic appTokenQueryResponse = client.Get("oauth/access_token"
, new
{
client_id = appId,
client_secret = appSecret,
grant_type = "client_credentials"
});
_appAccessToken = appTokenQueryResponse.access_token;
Step 2
With the server access token, We're able to make the appropriate calls into the API in order to make sure the token is valid.
private FacebookAuthorizationResponse AuthorizeUser(FacebookClient client, string userId, string accessToken)
{
dynamic expirationToken = client.Get("debug_token", new
{
input_token = accessToken,
access_token = _appAccessToken
});
DateTime expiresAt = DateTimeConvertor.FromUnixTime(expirationToken.data.expires_at);
bool isValid = expirationToken.data.is_valid;
if (!isValid)
{
return new FacebookAuthorizationResponse
{
IsAuthorized = false,
};
}
dynamic response = client.Get(userId, new
{
access_token = accessToken,
fields = "id,name"
});
return new FacebookAuthorizationResponse
{
IsAuthorized = isValid,
ExpiresAt = expiresAt,
Name = response.name
};
}
Where
public class FacebookAuthorizationResponse
{
public bool IsAuthorized { get; set; }
public DateTime ExpiresAt { get; set; }
public string Name { get; set; }
}
I'm using ASP.Net Identity to implement external logins. After user logins in with Google I get google's external access token. I then make a second api call to ObtainLocalAccessToken() which trades the external access token for a new local one.
ObtainLocalAccessToken() calls VerifyExternalAccessToken() which verifies the external access token with the provider by manually making http calls and parsing the user_id.
How can I leverage ASP.NET identity to remove the entire method VerifyExternalAccessToken()?
I believe that's what [HostAuthentication(DefaultAuthenticationTypes.ExternalBearer)] is for isn't it? I want to decorate ObtainLocalAccessToken() endpoint with that attribute and send the external_access_token in the header ({'Authorization' : 'Bearer xxx' }), and it should populate User.Identity without needing to manually verify the external access token? I believe that’s the purpose, however I cannot get it working. I send a valid external access token from google and it gets rejected with a 401.
I have this line in Startup.Auth btw:
app.UseOAuthBearerTokens(new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(),
AuthorizeEndpointPath = new PathString("/AccountApi/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
AllowInsecureHttp = true
});
Alternatively, it is possible to use "/Token" endpoint to trade an external access token for a local one? Which approach is correct?
Studying the implementation by Taiseer Joudeh
the /ExternalLogin endpoint replaces the OWIN Authentication Challenge.
The AngularJS LoginController makes a call to the authService.obtainAccessToken when an externally authenticated user has not been found in Identity Provider:
if (fragment.haslocalaccount == 'False') {
...
}
else {
//Obtain access token and redirect to orders
var externalData = { provider: fragment.provider,
externalAccessToken: fragment.external_access_token };
authService.obtainAccessToken(externalData).then(function (response) {
$location.path('/orders');
It uses the VerifyExternalAccessToken to perform a reverse lookup against Google and Facebook API's to get claim info for the bearer token.
if (provider == "Facebook")
{
var appToken = "xxxxxx";
verifyTokenEndPoint = string.Format("https://graph.facebook.com/debug_token?input_token={0}&access_token={1}", accessToken, appToken);
}
else if (provider == "Google")
{
verifyTokenEndPoint = string.Format("https://www.googleapis.com/oauth2/v1/tokeninfo?access_token={0}", accessToken);
}
else
{
return null;
}
If token is found, it returns a new ASP.NET bearer token
var accessTokenResponse = GenerateLocalAccessTokenResponse(user.UserName);
return Ok(accessTokenResponse);
With [HostAuthentication(DefaultAuthenticationTypes.ExternalBearer)] the OWIN Middleware uses the external bearer token to access the 3rd party's Cookie and Register a new account (Or find existing).
OWIN Middleware cannot be configured to accept external bearer token instead of local authority tokens. External bearer tokens are only used for Authentication and Registration.