Obtaining Access token from refresh token using Google API - c#-4.0

I have been using google+ api for .NET in my application.Using the example provided in this
site i am able to get the access token.
My problem is how to obtain the access token from the refresh token using OAuth 2.0.I haven't found any examples to get the access token from refresh token.
I have referred the [google+ API reference] but they have mentioned it using HTTP methods.2Please provide me some examples in C# using the methods provided by google+ APIs.

For the first time, you need to get the access token from the browser prompt and then save it in some store.
Check if the token is expired and then try to refresh it.
Code here :
private static IAuthorizationState GetAuthentication(NativeApplicationClient arg)
{
try
{
// Get the auth URL:
var config = new Configuration();
var calendarScope = Google.Apis.Util.Utilities.ConvertToString(CalendarService.Scopes.Calendar);
IAuthorizationState state = new AuthorizationState(new[] { calendarScope });
state.Callback = new Uri(NativeApplicationClient.OutOfBandCallbackUrl);
Uri authUri = arg.RequestUserAuthorization(state);
var authCode = String.Empty;
if (String.IsNullOrWhiteSpace(config.AccessToken) || config.AccessTokenExpirationTime < DateTime.Now || String.IsNullOrWhiteSpace(config.RefreshToken))
{
// Request authorization from the user (by opening a browser window):
Process.Start(authUri.ToString());
do
{
authCode = Prompt.ShowDialog("Test", "123");
} while (String.IsNullOrWhiteSpace(authCode));
state = arg.ProcessUserAuthorization(authCode, state);
}
else
{
state.AccessToken = config.AccessToken;
state.AccessTokenExpirationUtc = config.AccessTokenExpirationTime;
state.AccessTokenIssueDateUtc = config.AccessTokenIssueTime;
state.RefreshToken =config.RefreshToken ;
if (state.AccessTokenExpirationUtc < DateTime.Now)
{
var tokenRefreshed = arg.RefreshToken(state);
if (tokenRefreshed)
{
config.AccessToken = state.AccessToken;
config.AccessTokenExpirationTime = state.AccessTokenExpirationUtc;
config.AccessTokenIssueTime = state.AccessTokenIssueDateUtc;
config.RefreshToken = state.RefreshToken;
arg.ProcessUserAuthorization(authCode, state);
}
else
{
throw new ApplicationException("Unable to refresh the token.");
}
}
}
return state;
}
catch (System.Exception exp)
{
throw new ApplicationException("Failed to get authorisation from Google Calender.", exp);
}
}

Related

RequestJWTUserToken method occasionally seems to be returning wrong AccessToken

I'm using RequestJWTUserToken (docusign.esign.dll file version 4.1.1.0) to generate access tokens for a web application for each user, however occasionally the web application is sending documents from the wrong User's account.
I have logged the steps of the application, and when the error occurs, 2 (or more) of the my users are authenticating their UserIDs through our Integrator key at about the exact same time.
Each user is re-authenticating each time it is time to send a document. The logs that I'm writing show that I am sending in the correct GUID in the parameter list to the RequestJWTUserToken method. Any ideas on what could be occurring?
The method snippet is:
private OAuth.OAuthToken UpdateToken(string strUserGuid)
{
try
{
string ServicePointManager_SecurityProtocol_TLS_12 = ConfigurationManager.AppSettings["ServicePointManager_SecurityProtocol_TLS_12"];
System.Collections.Specialized.NameValueCollection obj = ConfigurationManager.AppSettings;
if (ServicePointManager_SecurityProtocol_TLS_12 == null || ServicePointManager_SecurityProtocol_TLS_12.Equals("true", StringComparison.OrdinalIgnoreCase)) ServicePointManager.SecurityProtocol = Tls12;
System.Net.ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
var mtype = ServicePointManager.SecurityProtocol;
OAuth.OAuthToken authToken =
ApiClient.RequestJWTUserToken(IntegratorKey,
strUserGuid,
AuthServer,
PrivateKey,
1);
AccessToken = authToken.access_token;
//set Account Property
if (Account == null) Account = GetAccountInfo();
ApiClient = new ApiClient(Account.BaseUri + "/restapi");
ExpiresIn = DateTime.Now.Second + authToken.expires_in.Value;
return authToken;
}
catch (ApiException ex)
{
if (ex.Source.Contains("DocuSign.eSign") && strUserGuid != "")
{
if (ex.Message.Contains("consent_required")) //send email with consent to sign link
throw new Exception("DocuSign Consent Required for " + HttpContext.Current.Session["USER_FULLNAME"]);
else
switch (ex.ErrorCode)
{
case 400: throw new Exception("Bad Request: The requested UserID may exist in DocuSign, but there is an issue with the Docusign User Account.");
default: throw new Exception("General Error");
}
}
else throw;
}
catch
{
throw;
}
}

Authentication problem - secure web application with API auth server

I have started to create a software architecture where i have:
Auth_API - as an auth server
Resource_API - as a resource API (protected with Auth_API)
WebApplication (mvc) - a frontend application (protected with Auth_API).
Based on https://bitoftech.net/2014/06/01/token-based-authentication-asp-net-web-api-2-owin-asp-net-identity/ article I have successfully made a Google authentication.
WebApp == redirects to ==> Auth_API == challenge ==> Google ==> API receives externalAccessToken, registers user locally and returns localAccessToken
Now everything is OK when I would want to use bearer authorization (using local access token).
But I also want to sign in to by ASP MVC application with (cookie?) ClaimsIdentity.
I was thinking about switching to JWT, but I am not sure which way I should go...
Bit of code:
Auth_API - obtain local access token
/// <summary>
/// Returns local access token for already registered users
/// </summary>
/// <param name="provider"></param>
/// <param name="externalAccessToken"></param>
/// <returns></returns>
[AllowAnonymous]
[HttpGet]
[Route("ObtainLocalAccessToken")]
public async Task<IHttpActionResult> ObtainLocalAccessToken(string provider, string externalAccessToken)
{
if (string.IsNullOrWhiteSpace(provider) || string.IsNullOrWhiteSpace(externalAccessToken))
{
return BadRequest("Provider or external access token is not sent");
}
var verifiedAccessToken = await VerifyExternalAccessToken(provider, externalAccessToken);
if (verifiedAccessToken == null)
{
return BadRequest("Invalid Provider or External Access Token");
}
IdentityUser user = await _repo.FindAsync(new UserLoginInfo(provider, verifiedAccessToken.user_id));
bool hasRegistered = user != null;
if (!hasRegistered)
{
return BadRequest("External user is not registered");
}
//generate access token response
var accessTokenResponse = GenerateLocalAccessTokenResponse(user.UserName);
return Ok(accessTokenResponse);
}
Generate Local Access Token algorythm
private JObject GenerateLocalAccessTokenResponse(string userName)
{
var tokenExpiration = TimeSpan.FromDays(1);
ClaimsIdentity identity = new ClaimsIdentity(OAuthDefaults.AuthenticationType);
identity.AddClaim(new Claim(ClaimTypes.Name, userName));
identity.AddClaim(new Claim("role", "user"));
var props = new AuthenticationProperties()
{
IssuedUtc = DateTime.UtcNow,
ExpiresUtc = DateTime.UtcNow.Add(tokenExpiration),
};
var ticket = new AuthenticationTicket(identity, props);
var accessToken = Startup.OAuthBearerOptions.AccessTokenFormat.Protect(ticket);
JObject tokenResponse = new JObject(
new JProperty("userName", userName),
new JProperty("access_token", accessToken),
new JProperty("token_type", "bearer"),
new JProperty("expires_in", tokenExpiration.TotalSeconds.ToString()),
new JProperty(".issued", ticket.Properties.IssuedUtc.ToString()),
new JProperty(".expires", ticket.Properties.ExpiresUtc.ToString())
);
return tokenResponse;
}
Web application - part of login action:
if (hasLocalAccount)
{
var client = new RestClient(baseApiUrl);
var externalLoginUrl = "Account/ObtainLocalAccessToken";
var externalLoginRequest = new RestRequest(externalLoginUrl, Method.GET);
externalLoginRequest.AddQueryParameter("provider", provider);
externalLoginRequest.AddQueryParameter("externalAccessToken", externalAccessToken);
var externalLoginResponse = client.Execute(externalLoginRequest);
if (externalLoginResponse.IsSuccessful)
{
JObject response = JObject.Parse(externalLoginResponse.Content);
string localAccessToken = response["access_token"].Value<string>();
string localTokenExpiresIn = response["expires_in"].Value<string>();
// WHAT TO DO WHERE TO SIGN IN A USER ???
//AuthenticationTicket ticket = Startup.OAuthBearerOptions.AccessTokenFormat.Unprotect(localAccessToken); <== this returns NULL
return RedirectToAction("Index", "Home");
}
}

Re-authorize persistent login (MVC client) without triggering login UI

I am trying to figure out how a server-side client (MVC / ASP.NET Core 2) can query IdentityServer4 to retrieve various claims scopes for a persistent login created in some previous session without prompting for login if the persistent login is invalid (user inactive, cookie expired, etc).
We're using Implicit flow with third-party auth (Google, FB, etc) but we changed the session-duration on the cookie to a more user-friendly 30 day expiration in IdentityServer's ExternalLoginCallback.
Accessing claims on HttpContext.User (we are not using ASP.NET Identity) works great during the session that establishes login. On some later session, navigating to a client resource with an [Authorize] attribute also works: if the user had logged in previously, they transparently gain access to the resource, claims are populated, etc. If not, they're prompted for login, which is ok in response to a user-initiated action.
However, we have a requirement for the client landing page to alter the content depending on whether the user is anonymous or authenticated. A simple example would be "Register" and "Log in" links for anonymous users, but "Account" and "Log out" links for authenticated users.
Hence the reason to retrieve claims and jump-start the persistent login if it's valid, but do nothing (no login prompt) if invalid: we don't want the landing page to force every anonymous user to a login screen.
Nothing special to say about our setups on either end of the pipeline. Client:
services.AddAuthentication(options =>
{
options.DefaultScheme = "Cookies";
options.DefaultChallengeScheme = "oidc";
})
.AddCookie("Cookies")
.AddOpenIdConnect("oidc", options =>
{
options.SignInScheme = "Cookies";
options.Authority = "https://localhost:5000";
options.RequireHttpsMetadata = true;
options.ClientId = "example.com.webserver";
options.ClientSecret = "examplesecret";
options.ResponseType = "id_token";
options.SaveTokens = true;
options.GetClaimsFromUserInfoEndpoint = true;
options.Scope.Add("example.com.identity");
});
IdentityServer client resource definition:
new Client
{
ClientId = "example.com.webserver",
ClientName = "example.com",
ClientUri = "https://localhost:5002",
AllowedGrantTypes = GrantTypes.Implicit,
ClientSecrets = {new Secret("examplesecret".Sha256())},
RequireConsent = false,
AllowRememberConsent = true,
AllowOfflineAccess = true,
RedirectUris = { "https://localhost:5002/signin-oidc"},
PostLogoutRedirectUris = { "https://localhost:5002/signout-callback-oidc"},
AllowedScopes = new List<string>
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email,
IdentityServerConstants.StandardScopes.Phone,
IdentityServerConstants.StandardScopes.Address,
"example.com.identity"
}
}
The client runs an intentional login (user clicks "Log in" link) like so:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task Login()
{
await HttpContext.SignOutAsync("oidc");
await HttpContext.ChallengeAsync("oidc",
new AuthenticationProperties() {
RedirectUri = Url.Action("LoginCallback")
});
}
The solution is to add a second OIDC auth flow in Setup, intercept the redirect to change the Prompt option to none so that no login prompt is shown, intercept the resulting login_required error message, and trigger that flow in the landing page's PageModel OnGet handler (the client app uses RazorPages).
One caveat is the handler must set flags so that this is only attempted once, and so that it can detect whether the page is being hit for the first time, or as the return-trip from the login attempt. This is achieved by just dropping a value into the Razor TempData which is just a cookie-based bucket of name-value pairs.
Add to Setup.cs
.AddOpenIdConnect("persistent", options =>
{
options.CallbackPath = "/signin-persistent";
options.Events = new OpenIdConnectEvents
{
OnRedirectToIdentityProvider = context =>
{
context.ProtocolMessage.Prompt = "none";
return Task.FromResult<object>(null);
},
OnMessageReceived = context => {
if(string.Equals(context.ProtocolMessage.Error, "login_required", StringComparison.Ordinal))
{
context.HandleResponse();
context.Response.Redirect("/");
}
return Task.FromResult<object>(null);
}
};
options.SignInScheme = "Cookies";
options.Authority = "https://localhost:5000";
options.RequireHttpsMetadata = true;
options.ClientId = "example.com.webserver";
options.ClientSecret = "examplesecret";
options.ResponseType = "code";
options.SaveTokens = true;
options.GetClaimsFromUserInfoEndpoint = true;
options.Scope.Add("example.com.identity");
})
Landing page (Index.cshtml.cs)
public class IndexModel : PageModel
{
private bool PersistentLoginAttempted = false;
private const string PersistentLoginFlag = "persistent_login_attempt";
public IActionResult OnGet()
{
// Always clean up an existing flag.
bool FlagFound = false;
if(!String.IsNullOrEmpty(TempData[PersistentLoginFlag] as string))
{
FlagFound = true;
TempData.Remove(PersistentLoginFlag);
}
// Try to refresh a persistent login the first time an anonymous user hits the index page in this session
if(!User.Identity.IsAuthenticated && !PersistentLoginAttempted)
{
PersistentLoginAttempted = true;
// If there was a flag, this is the return-trip from a failed persistent login attempt.
if(!FlagFound)
{
// No flag was found. Create it, then begin the OIDC challenge flow.
TempData[PersistentLoginFlag] = PersistentLoginFlag;
return Challenge("persistent");
}
}
return Page();
}
}
why not just make the authentication cookie permanent? Here's an other way to do it ... You can then check authentication on the server against the authentication server.
on the client AddOpenIdConnect :
options.Events = new OpenIdConnectEvents {
OnRedirectToIdentityProvider = context => {
context.Properties.RedirectUri = context.Request.Path;
return Task.FromResult(0);
},
OnTicketReceived = context =>
{
context.Properties.IsPersistent = true;
context.Properties.ExpiresUtc = DateTimeOffset.UtcNow.AddDays(15);
context.Properties.AllowRefresh = true;
return Task.FromResult(0);
}
};

IdentityServer3 Refresh Token - Where to invoke the refresh

I am developing a MVC application that uses OpenID and IdentityServer3.
Background:
I am running into a issue that when the Authentication Cookie times out, I need to use the refresh token to generate a new one.
I am able to login and receive the AuthorizationCodeReceived notification, which i use to request an authorization code and retrieve a RefreshToken which I store in the claims of the AuthenticationTicket.
I have tried adding logic to check and refresh the authentication in:
CookieAuthenticationProvider.OnValidateIdentity -- This works to
refresh, and I was able to update the cookie, but it is not called after the cookie expired.
Adding code in the begining of the the ResourceAuthorizationManager.CheckAccessAsync -- this does not work because the identity is null and I cannot retrieve the refresh token claim.
Adding a filter Filter for MVC, but I am unable to figure out what to add as a HttpResponseMessage for WebAPI.
public const string RefreshTokenKey = "refresh_token";
public const string ExpiresAtKey = "expires_at";
private const string AccessTokenKey = "access_token";
private static bool CheckAndRefreshTokenIfRequired(ClaimsIdentity id, out ClaimsIdentity identity)
{
if (id == null)
{
identity = null;
return false;
}
if (id.Claims.All(x => x.Type != ExpiresAtKey) || id.Claims.All(x => x.Type != RefreshTokenKey))
{
identity = id;
return false;
}
//Check if the access token has expired
var expiresAt = DateTime.Parse(id.FindFirstValue(ExpiresAtKey));
if ((expiresAt - DateTime.Now.ToLocalTime()).TotalSeconds < 0)
{
var client = GetClient();
var refreshToken = id.FindFirstValue(RefreshTokenKey);
var tokenResponse = client.RequestRefreshTokenAsync(refreshToken).Result;
if (tokenResponse.IsError)
{
throw new Exception(tokenResponse.Error);
}
var result = from c in id.Claims
where c.Type != AccessTokenKey &&
c.Type != RefreshTokenKey &&
c.Type != ExpiresAtKey
select c;
var claims = result.ToList();
claims.Add(new Claim(AccessTokenKey, tokenResponse.AccessToken));
claims.Add(new Claim(ExpiresAtKey, DateTime.Now.AddSeconds(tokenResponse.ExpiresIn).ToLocalTime().ToString()));
claims.Add(new Claim(RefreshTokenKey, tokenResponse.RefreshToken));
identity = new ClaimsIdentity(claims, id.AuthenticationType);
return true;
}
identity = id;
return false;
}
Links:
How would I use RefreshTokenHandler?
Identity Server3 documentation
Looked at the two examples, but using resourceowner flow for openid doesn't seem the right way. The MVC code flow relies on the User still having the principle, but my claims are all empty in the resource authorize.
EDIT:
Okay, so if I set the AuthenticationTicket.Properties.ExpiresUtc to null in AuthorizationCodeReceived, it is setting it to null then somewhere down the line it is setting it to 30 days instead of 5 minutes (I searched the katana and identity server source code but could not find where it is setting this value), which I can live with, but would prefer it to be the same as the browser where it is "Session"
app.UseCookieAuthentication(new CookieAuthenticationOptions()
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
CookieManager = new SystemWebChunkingCookieManager(),
Provider = new CookieAuthenticationProvider()
{
OnValidateIdentity = context =>
{
ClaimsIdentity i;
if (CheckAndRefreshTokenIfRequired(context.Identity, out i))
{
context.ReplaceIdentity(i);
}
return Task.FromResult(0);
}
}
});
The problem was that in the AuthorizationCodeRecieved notification I was passing the Properties from the original ticket, which had the timeout set for Expires for the authorization code Changing the the code to pass null in resolved the issue and allowed the CookieAuthenticationHandler.ApplyResponseGrantAsync to pass its own properties.
var claimsIdentity = new ClaimsIdentity(id.Claims, n.AuthenticationTicket.Identity.AuthenticationType, "name", "role");
n.AuthenticationTicket = new AuthenticationTicket(claimsIdentity, null);

Azure DocumentDB: Time-bound resource token remains valid after expiration time

I'm facing a strange problem in DocumentDb.
I created a user and permission for manipulating a specific collection. Then I requested a resource token with a limited validity for that user. I did this by setting ResourceTokenExpirySeconds to say 10 seconds. To test it, I waited enough in order the token got expired. However, Azure accepted subsequent requests using that (supposedly expired) token.
Considering the following code, it is expected the this line fails but it doesn't:
UserPermissionExample.TestToken(10, 15);
And here is UserPermissionExample implementation:
public class UserPermissionExample
{
const string endPoint = "https://[ENDPOINT].documents.azure.com";
const string masterKey = "[PRIMARY-KEY]";
const string dbName = "test-db";
const string testUserId = "test-user";
const string collName = "test-coll";
public static async Task TestToken(int validity, int wait)
{
// Get a time-bound token
string token = await GetToken(validity /*seconds*/);
// Wait enough so the token gets expired
Thread.Sleep(TimeSpan.FromSeconds(wait));
// Try to add a document with the expired token. It is expected to fail:
DocumentClient client = new DocumentClient(new Uri(endPoint), token);
await client.CreateDocumentAsync(
UriFactory.CreateDocumentCollectionUri(dbName, collName),
new { name = "A test document" });
}
static async Task<string> GetToken(int tokenValiditySeconds)
{
DocumentClient client = new DocumentClient(new Uri(endPoint), masterKey);
ResourceResponse<Database> db = await UpsertDb(client);
ResourceResponse<DocumentCollection> collection = await UpsertCollection(client, db);
var userUrl = UriFactory.CreateUserUri(dbName, "testuser");
var user = await client.UpsertUserAsync(db.Resource.SelfLink, new User() { Id = testUserId });
var permission =
await client.UpsertPermissionAsync(
user.Resource.SelfLink,
new Permission()
{
Id = "PersmissionForTestUser",
PermissionMode = PermissionMode.All,
ResourceLink = collection.Resource.SelfLink,
},
new RequestOptions() { ResourceTokenExpirySeconds = tokenValiditySeconds });
permission =
await client.ReadPermissionAsync(
permission.Resource.SelfLink,
new RequestOptions() { ResourceTokenExpirySeconds = tokenValiditySeconds });
return permission.Resource.Token;
}
static async Task<ResourceResponse<DocumentCollection>> UpsertCollection(DocumentClient client, ResourceResponse<Database> db)
{
ResourceResponse<DocumentCollection> collection = null;
try
{
collection = await client.ReadDocumentCollectionAsync(UriFactory.CreateDocumentCollectionUri(dbName, collName));
}
catch (DocumentClientException ex)
{
if (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
{
collection = await client.CreateDocumentCollectionAsync(db.Resource.SelfLink, new DocumentCollection() { Id = collName });
}
}
return collection;
}
static async Task<ResourceResponse<Database>> UpsertDb(DocumentClient client)
{
ResourceResponse<Database> db = null;
try
{
db = await client.ReadDatabaseAsync(UriFactory.CreateDatabaseUri(dbName));
}
catch (DocumentClientException ex)
{
if (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
{
db = await client.CreateDatabaseAsync(new Database() { Id = dbName });
}
}
return db;
}
}
PS: I'm using Microsoft.Azure.DocumentDB version="1.11.4".
Update: I found that Azure starts checking token expiry from 5 minutes after token issue time ! Then it rejects the token. So prior to this period, no matter token is expired or not, Azure accepts it!
The reason for this is that DocumentDB has some slack in the token expiry to allow for potential clock skews in the client or server. Because of this tokens have a "grace period" after their real expiry time. The allowed clock skew is in the tune of 5 minutes which is why you see the behaviour that you do.
Is there a particular reason you're creating a token that should be valid only for 10 seconds?
Full disclosure: I work at DocumentDB.

Resources