How to access session in OnAuthorizationCodeReceived - asp.net-mvc

I am not sure ,but is it possible to access session variable in OnAuthorizationCodeReceived callback while getting access token from.
My requirement is like using DBConnection ,to store token in database . As Session["DBConnection "] is always null.
Using ASP.NET MVC 5.
Thanks
public void SignIn()
{
Session["DBConnection "] = GetConnectionObject();
// Send an OpenID Connect sign-in request.
if (!Request.IsAuthenticated)
{
HttpContext.GetOwinContext().Authentication.Challenge(new AuthenticationProperties { RedirectUri = "/" },
OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
}
In Startup.Auth.cs
private async Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedNotification context)
{
// Upon successful sign in, get the access token & cache it using MSAL
IConfidentialClientApplication clientApp = await MsalAppBuilder.BuildConfidentialClientApplication();
AuthenticationResult authResult = await clientApp.AcquireTokenByAuthorizationCode(new[] { "User.Read" }, context.Code).ExecuteAsync();
string token = authResult?.IdToken;
if (token != null)
{
/* connection is always null*/
var connection= HttpContext.Current.Session["DBConnection "]
context.HandleResponse();
context.Response.Redirect(context.RedirectUri);
}
}

Related

Calling Microsoft Graph API using first party app fails with insufficient privileges to complete the operation on "SubscribedSkus API"

Calling microsoft graph API https://graph.microsoft.com/v1.0/subscribedSkus fails with
"code": "Authorization_RequestDenied",
"message": "Insufficient privileges to complete the operation.",
This is happening if we create a new user in the tenant who is non admin. But while calling this with Admin user it works just fine. Even it works for any microsoft user in the tenant.
This is the below code I used to try.
public static async Task TestAadGraph()
{
// using obo token of the user.
var graphToken = await GetTokenAsync(UserId, Token, "https://graph.microsoft.com");
var aadGraphClient = new AadGraphClient(new HttpClient());
var licenseResponse = await aadGraphClient.GetTenantLicenseDetailAsync(graphToken);
foreach (var license in licenseResponse)
{
Console.WriteLine("Sku ID: {0}", license.SkuId);
Console.WriteLine("Sku Part Number: {0}", license.SkuPartNumber);
foreach (var plan in license.ServicePlans)
{
Console.WriteLine("Plan Id: {0}", plan.ServicePlanId);
Console.WriteLine("Plan Name: {0}", plan.ServicePlanName);
}
}
}
public async Task<SubscribedSku[]> GetTenantLicenseDetailAsync(string accessToken)
{
var request = new RequestMessage
{
BearerToken = accessToken,
Endpoint = new Uri("http://graph.microsoft.com/v1.0/subscribedSkus"),
};
var response = await _httpClient.FetchAsync<SubscribedSkusResponse>(request);
return response.Value;
}
public static async Task<T> FetchAsync<T>(this HttpClient httpClient, RequestMessage request, Action<HttpResponseMessage, string> responseCallback) where T : class
{
request.Method = request.Method ?? HttpMethod.Get;
request.MediaType = request.MediaType ?? "application/json";
using (HttpRequestMessage message = new HttpRequestMessage(request.Method,
UrlHelper.AppendParameters(request.Params, request.Endpoint)))
{
if (!string.IsNullOrEmpty(request.BearerToken))
{
message.Headers.Authorization = new AuthenticationHeaderValue("Bearer",
request.BearerToken);
}
if (request.Headers != null)
{
foreach (KeyValuePair<string, string> header in request.Headers)
{
message.Headers.Add(header.Key, header.Value);
}
}
if (!string.IsNullOrEmpty(request.Content))
{
message.Content = new StringContent(request.Content, Encoding.UTF8,
request.MediaType);
}`
using (HttpResponseMessage response = await httpClient.SendAsync(message))
{
string json = await response.Content.ReadAsStringAsync();
if (responseCallback != null)
{
responseCallback?.Invoke(response, json);
}
if (response.IsSuccessStatusCode)
{
if (predictor != null)
{
json = predictor(JToken.Parse(json)).ToString();
}
return JsonConvert.DeserializeObject<T>(json);
}
else
{
throw new WebRequestException(response, json);
}
}
}
}
Firstly, try the same call for the new created user in Microsoft Graph Explorer to see if the same scene exists. If not, it means the there is nothing wrong with the new user.
Then debug your code and copy the graphToken into https://jwt.io/ and see if the Decoded result has one of the required Delegated permissions:Organization.Read.All, Directory.Read.All, Organization.ReadWrite.All, Directory.ReadWrite.All, Directory.AccessAsUser.All. Note that the "upn" should be the username of the new created user.
If the required permissions do not exist, you will need to assign permissions in the Azure AD app. See API permissions.
Perfect. Adding permission to the first party app has actually worked.

JWT Token authentication - Doing it right way

Overview of my project structure:
I have 2 Projects.
Asp.net core Web Api
Asp.net core Web MVC
In Web Api Project
I am NOT using Asp.net core Identity for login, instead, I am using my own login mechanism.
LoginAction method will authenticate user in database and generate JWT Token.
I was able to generate JWT Token and Life is smooth till this point.
Generate Token
[AllowAnonymous]
[Route("requesttoken")]
[HttpPost]
public async Task<IActionResult> RequestToken([FromBody] TokenRequest request)
{
var result = await IsValidUser(request);
if(result)
{
var claims = new[]
{
new Claim(ClaimTypes.Name, request.Email)
};
var key = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(_myAppSettings.SecurityKey));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(
issuer: _myAppSettings.WebsiteName.ToLower(),
audience: _myAppSettings.WebsiteName.ToLower(),
claims: claims,
notBefore: Utilities.GetEST_DateTimeNow(),
expires: Utilities.GetEST_DateTimeNow().AddMinutes(5),
signingCredentials: creds);
return Ok(new
{
token = new JwtSecurityTokenHandler().WriteToken(token)
});
}
else
{
return Unauthorized();
}
}
Inside Startup class
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.Configure<MyAppSettings>(Configuration.GetSection("MyAppSettings"));
#region Validate JWT Token
ConfigureJwtAuthService(services, Configuration);
#endregion
services.AddMvc();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseAuthentication();
app.UseMvc();
}
JWT Validation part (As partial startup class)
public void ConfigureJwtAuthService(IServiceCollection services, IConfiguration configuration)
{
var symmetricKeyAsBase64 = configuration["MyAppSettings:SecurityKey"];
var keyByteArray = Encoding.ASCII.GetBytes(symmetricKeyAsBase64);
var signingKey = new SymmetricSecurityKey(keyByteArray);
var tokenValidationParameters = new TokenValidationParameters
{
// The signing key must match!
ValidateIssuerSigningKey = true,
IssuerSigningKey = signingKey,
// Validate the JWT Issuer (iss) claim
ValidateIssuer = true,
ValidIssuer = Configuration["MyAppSettings:WebsiteName"].ToLower(),
// Validate the JWT Audience (aud) claim
ValidateAudience = true,
ValidAudience = Configuration["MyAppSettings:WebsiteName"].ToLower(),
// Validate the token expiry
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero
};
services.AddAuthentication(
options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(o => o.TokenValidationParameters = tokenValidationParameters);
}
Sample response of LoginAction Method.
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkrDtGhuIETDs8OoIiwiYWRtaW4iOnRydWV9.469tBeJmYLERjlKi9u6gylb-2NsjHLC_6kZNdtoOGsA"
}
In Web MVC Project
I am consuming above Web Api and passing login parameters and was able to get JWT Token response.
I am storing JWT Token response in cookie [Manually - _httpContextAccessor.HttpContext.Response.Cookies.Append(key, jwtTokenValue, option);]
Based on JWT Token response receive, I am trying to extract claims from that JWT Token, so that I can able to create valid identity of user and login user on the web.
I am trying to achieve something like below:
{
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, model.Email)
};
var userIdentity = new ClaimsIdentity(claims, "login");
ClaimsPrincipal principal = new ClaimsPrincipal(userIdentity);
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal);
return RedirectToLocal(returnUrl);
}
Questions
Am I doing right by storing JWT Token in cookie. Is my manual approach of storing cookie is correct or is there any better way?
How can I get claims from JWT in Web Project, so that I can able to singin user using cookie?
Want to do it right way, any help would be much appreciated.
Following seems to helped me: http://blogs.quovantis.com/json-web-token-jwt-with-web-api/ not sure whether that is right way of doing or not.
/// Using the same key used for signing token, user payload is generated back
public JwtSecurityToken GenerateUserClaimFromJWT(string authToken)
{
var tokenValidationParameters = new TokenValidationParameters()
{
ValidAudiences = new string[]
{
"http://www.example.com",
},
ValidIssuers = new string[]
{
"self",
},
IssuerSigningKey = signingKey
};
var tokenHandler = new JwtSecurityTokenHandler();
SecurityToken validatedToken;
try {
tokenHandler.ValidateToken(authToken,tokenValidationParameters, out validatedToken);
}
catch (Exception)
{
return null;
}
return validatedToken as JwtSecurityToken;
}

Troubles authenticating native client to Azure AD securised MVC web app

I created both a MVC 5 web app hosted on Azure and a WPF client. My short term purpose (as if I can achieve that I'll be able to implement all my uses case) is the following:
Enforce Azure Ad authentification on the WPF client
Have the MVC web app to check through Azure Graph API the AD group membership of the user authentified in the client
Send back Graph API object to the client (IUser, Group...)
Use group membership to define Authorization on controllers
My actual issue is the following:
The user launch the app, and is prompted for authentication. I guess it work as I can display the user's mail and I have an access token.
The user tries to access a web api controller and it works fine
The user tries to access another web api controller decorated with [Authorize] and i get in return some HTML page stating this : "We can't sign you in.Your browser is currently set to block JavaScript. You need to allow JavaScript to use this service."
From what I've found searching on the web it seems that it could be related to my web app that is not configured properly (I already tried to add my webapp url in trusted sites and I'm sure that my controller URL is Ok). i cannot find much doc on native client + AAD + MVC so I don't really know how to correct it.
Here's my startup.auth.cs from the webapp :
public partial class Startup
{
private static string clientId = ConfigurationManager.AppSettings["ida:ClientId"];
private static string appKey = ConfigurationManager.AppSettings["ida:AppKey"];
private static string aadInstance = ConfigurationManager.AppSettings["ida:AADInstance"];
private static string tenant = ConfigurationManager.AppSettings["ida:Tenant"];
private static string tenantId = ConfigurationManager.AppSettings["ida:TenantId"];
private static string postLogoutRedirectUri = ConfigurationManager.AppSettings["ida:PostLogoutRedirectUri"];
private static string certName = ConfigurationManager.AppSettings["ida:CertName"];
public static readonly string Authority = String.Format(CultureInfo.InvariantCulture, aadInstance, tenant);
string graphResourceId = ConfigurationManager.AppSettings["ida:GraphUrl"];
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = Authority,
PostLogoutRedirectUri = postLogoutRedirectUri,
Notifications = new OpenIdConnectAuthenticationNotifications()
{
//
// If there is a code in the OpenID Connect response, redeem it for an access token and refresh token, and store those away.
//
AuthorizationCodeReceived = (context) =>
{
var code = context.Code;
#region Certs (not used)
if (certName.Length != 0)
{
// Create a Client Credential Using a Certificate
//
// Initialize the Certificate Credential to be used by ADAL.
// First find the matching certificate in the cert store.
//
X509Certificate2 cert = null;
X509Store store = new X509Store(StoreLocation.CurrentUser);
try
{
store.Open(OpenFlags.ReadOnly);
// Place all certificates in an X509Certificate2Collection object.
X509Certificate2Collection certCollection = store.Certificates;
// Find unexpired certificates.
X509Certificate2Collection currentCerts = certCollection.Find(X509FindType.FindByTimeValid, DateTime.Now, false);
// From the collection of unexpired certificates, find the ones with the correct name.
X509Certificate2Collection signingCert = currentCerts.Find(X509FindType.FindBySubjectDistinguishedName, certName, false);
if (signingCert.Count == 0)
{
// No matching certificate found.
return Task.FromResult(0);
}
// Return the first certificate in the collection, has the right name and is current.
cert = signingCert[0];
}
finally
{
store.Close();
}
// Then create the certificate credential.
ClientAssertionCertificate credential = new ClientAssertionCertificate(clientId, cert);
string userObjectID = context.AuthenticationTicket.Identity.FindFirst(
"http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
AuthenticationContext authContext = new AuthenticationContext(Authority, new NaiveSessionCache(userObjectID));
AuthenticationResult result = authContext.AcquireTokenByAuthorizationCode(
code, new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path)), credential, graphResourceId);
AuthenticationHelper.token = result.AccessToken;
}
#endregion
else
{
// Create a Client Credential Using an Application Key
ClientCredential credential = new ClientCredential(clientId, appKey);
string userObjectID = context.AuthenticationTicket.Identity.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
AuthenticationContext authContext = new AuthenticationContext(Authority, new NaiveSessionCache(userObjectID));
AuthenticationResult result = authContext.AcquireTokenByAuthorizationCode(
code, new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path)), credential, graphResourceId);
AuthenticationHelper.token = result.AccessToken;
}
return Task.FromResult(0);
}
}
});
}
}
Here's the controller which can be acceded when not decorated with [Authorize] but in that case the action throw a null exception (but if I can't get it fixed i'll post another question):
[System.Web.Http.Authorize]
public class UserADGraphController : ApiController
{
[ResponseType(typeof(IUser))]
[System.Web.Http.Route("api/UserADGraphController/GetMyInformations")]
public IHttpActionResult GetMyInformations()
{
try
{
string uID = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
if (uID == null)
return Ok("UId null");
ActiveDirectoryClient client = AuthenticationHelper.GetActiveDirectoryClient();
if (client == null)
return Ok("Client null");
IUser adUser = client.Users.Where(u => u.ObjectId == uID).ExecuteAsync().Result.CurrentPage.SingleOrDefault();
if (adUser == null)
{
return NotFound();
}
return Ok(adUser);
}
catch (Exception e)
{
return Ok(e.Message + " " + e.StackTrace);
}
and finally here are relevant parts of the client:
In the mainviewmodel class:
#region Azure AD auth properties
private static string aadInstance = ConfigurationManager.AppSettings["ida:AADInstance"];
private static string tenant = ConfigurationManager.AppSettings["ida:Tenant"];
private static string clientId = ConfigurationManager.AppSettings["ida:ClientId"];
Uri redirectUri = new Uri(ConfigurationManager.AppSettings["ida:RedirectUri"]);
private static string authority = String.Format(CultureInfo.InvariantCulture, aadInstance, tenant);
private static string AppServiceResourceId = ConfigurationManager.AppSettings["todo:AppServiceResourceId"];
private static string AppServiceBaseAddress = ConfigurationManager.AppSettings["todo:AppServiceBaseAddress"];
private HttpClient httpClient;
private AuthenticationContext authContext = null;
#endregion
In the mainviewmodel constructor:
authContext = new AuthenticationContext(authority);
httpClient = new HttpClient();
My sign in method:
{
AuthenticationResult result = null;
try
{
result = authContext.AcquireToken(AppServiceResourceId, clientId, redirectUri, PromptBehavior.Auto);
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
SignInLabelContent = "Connected to azure AD as " + result.UserInfo.DisplayableId;
}
catch (AdalException ex)
{
if (ex.ErrorCode == "user_interaction_required")
{
}
else
{
// An unexpected error occurred.
string message = ex.Message;
if (ex.InnerException != null)
{
message += "Inner Exception : " + ex.InnerException.Message;
}
Messenger.Default.Send<NotificationMessage>(new NotificationMessage(message));
//MessageBox.Show(message);
}
return;
}
}
The method that access the protected controller:
IUser me = null;
AuthenticationResult result = null;
result = authContext.AcquireToken(AppServiceResourceId, clientId, redirectUri, PromptBehavior.Auto);
string authHeader = result.CreateAuthorizationHeader();
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
//HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, AppServiceBaseAddress + "/api/UserADGraphController/GetMyInformations");
//request.Headers.TryAddWithoutValidation("Authorization", authHeader);
//HttpResponseMessage response = await client.SendAsync(request);
//string responseString = await response.Content.ReadAsStringAsync();
//LogManager.log(responseString);
//Messenger.Default.Send<NotificationMessage>(new NotificationMessage(responseString));
HttpResponseMessage response = await httpClient.GetAsync(AppServiceBaseAddress + "/api/UserADGraphController/GetMyInformations");
if (response.IsSuccessStatusCode)
{
var jsonString = await response.Content.ReadAsStringAsync();
LogManager.log(jsonString);
me = JsonConvert.DeserializeObject<IUser>(jsonString);
//Messenger.Default.Send<NotificationMessage>(new NotificationMessage(jsonString));
}
In my case response has status code 200 but the jsonString contains the web page telling me about javascript disabled.
If someone has an idea it would be great !
Thanks !
If someone gets into this issue, I managed to solve it by changing my configureAuth method this way :
var azureADBearerAuthOptions = new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Tenant = tenant
};
azureADBearerAuthOptions.TokenValidationParameters = new TokenValidationParameters()
{
ValidAudience = audience
};
app.UseWindowsAzureActiveDirectoryBearerAuthentication(azureADBearerAuthOptions);
This error message is very misleading. I was getting the same problem, and found that my issue was actually mismatched Client Secret/AppURI settings.
From the error message, I assumed it was something I was doing in the ConfigureAuth method. Turns out I was mixing up dev and test settings.
Maybe this will help others who end up on this confusing error message.

OAuth access and refresh token control / management on user password change

We are in the process of developing a in house mobile application and web api.
We are using asp.net web api 2 with asp.net Identy 2 OAuth.
I have got the api up and running and giving me a bearer token. However I want to slightly modify the process flow to something like along the lines of this:
App user logs in to api with username and password.
App receives Refresh-token which is valid for 30 days.
App then requests an access token providing the api with the refresh token. ( Here I want to be able to invalidate a request if the user has changed their password or their account has been locked).
App Gets An Access token which is valid for 30 minutes or it gets a 401 if the password check failed.
The App can access the api with the given access token for the next 29 minutes. After that the app will have to get a new access token with the refresh token.
Reason I want to do this is in order to stop a users devices gaining access to the api after they have changed their password. If their phone gets stolen they need to be able to login to the website and change their password so the new owner of the phone cannot gain access to our companies services.
Is my proposed solution do able, and if so is it a sensible solution? I haven't forgotten any crucial elements?
I am willing to do a db access on ever token refresh, but not on every API call.
To summarize my questions are:
Is my planned method sensible?
How would I securely check if the password has changed or if account is locked in the refresh token process.
Please find my current OAuth Setups and classes below: (I have tried to add the refresh token functionality, but have not attempted to add any password verification yet)
Startup.Auth.cs
public partial class Startup
{
public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }
public static string PublicClientId { get; private set; }
// For more information on configuring authentication, please visit http://go.microsoft.com/fwlink/?LinkId=301864
public void ConfigureAuth(IAppBuilder app)
{
// Configure the db context and user manager to use a single instance per request
app.CreatePerOwinContext(IdentityDbContext.Create);
app.CreatePerOwinContext<FskUserManager>(FskUserManager.Create);
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
// Configure the application for OAuth based flow
PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(PublicClientId),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
RefreshTokenProvider = new ApplicationRefreshTokenProvider(),
AllowInsecureHttp = true
};
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions);
}
ApplicationRefreshTokenProvider.cs
public class ApplicationRefreshTokenProvider : AuthenticationTokenProvider
{
public override void Create(AuthenticationTokenCreateContext context)
{
// Expiration time in minutes
int refreshTokenExpiration = Convert.ToInt32(System.Configuration.ConfigurationManager.AppSettings["ApiRefreshTokenExpiry"]);
context.Ticket.Properties.ExpiresUtc = new DateTimeOffset(DateTime.Now.AddMinutes(refreshTokenExpiration));
context.SetToken(context.SerializeTicket());
}
public override void Receive(AuthenticationTokenReceiveContext context)
{
context.DeserializeTicket(context.Token);
}
}
ApplicationOAuthProvider.cs
public class ApplicationOAuthProvider : OAuthAuthorizationServerProvider
{
private readonly string _publicClientId;
public ApplicationOAuthProvider(string publicClientId)
{
if (publicClientId == null)
{
throw new ArgumentNullException("publicClientId");
}
_publicClientId = publicClientId;
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var userManager = context.OwinContext.GetUserManager<FskUserManager>();
FskUser user = await userManager.FindAsync(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager,
OAuthDefaults.AuthenticationType);
ClaimsIdentity cookiesIdentity = await user.GenerateUserIdentityAsync(userManager,
CookieAuthenticationDefaults.AuthenticationType);
AuthenticationProperties properties = CreateProperties(user.UserName);
AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
context.Validated(ticket);
context.Request.Context.Authentication.SignIn(cookiesIdentity);
}
public override Task TokenEndpoint(OAuthTokenEndpointContext context)
{
foreach (KeyValuePair<string, string> property in context.Properties.Dictionary)
{
context.AdditionalResponseParameters.Add(property.Key, property.Value);
}
return Task.FromResult<object>(null);
}
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
// Resource owner password credentials does not provide a client ID.
if (context.ClientId == null)
{
context.Validated();
}
return Task.FromResult<object>(null);
}
public override Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
{
if (context.ClientId == _publicClientId)
{
Uri expectedRootUri = new Uri(context.Request.Uri, "/");
if (expectedRootUri.AbsoluteUri == context.RedirectUri)
{
context.Validated();
}
}
return Task.FromResult<object>(null);
}
public static AuthenticationProperties CreateProperties(string userName)
{
IDictionary<string, string> data = new Dictionary<string, string>
{
{ "userName", userName }
};
return new AuthenticationProperties(data);
}
}
If I understood your task right, here is an idea.
On the create access token event, you can check if the password has been changed from the website and if so, revoke the refresh token. (you can create some flag that the password has been changed or something)
It should not be often when you are creating an access token so there should be no problems with the db access.
Now the question is how to revoke a refresh token. Unless there is a build in way you will have to implement a custom one. An idea here is to check the refresh token creation date and the date of the change password operation. If the change password operation is done after the creation of the refresh token, you do not authenticate the user.
Let me know what you think of this.

How to do long lived tokens with Katana OAuth Bearer Tokens

From the SPA template i managed to get basic OAuth flows working.
OAuthOptions = new OAuthAuthorizationServerOptions
{
AllowInsecureHttp = true,
ApplicationCanDisplayErrors = true,
TokenEndpointPath = new Microsoft.Owin.PathString("/Token"),
AuthorizeEndpointPath = new Microsoft.Owin.PathString("/api/Account/ExternalLogin"),
Provider = new CompositeWebroleOauthProvider<User>(PublicClientId, IdentityManagerFactory, CookieOptions)
};
I have a single page application that is hosted on a seperate domain that will interact with the webapi using the bearer tokens from the Token endpoint.
I am doing the ResourceOwnerCredentials flow, with a request with the following data:
data: {
grant_type: "password",
username: username,
password: password
}
These tokens are short lived ect. I now would like to extend my application such I can get a refress token or something such I do not have to authenticate all the time.
What is my next steps?
The GrantResourceOwnerCredentials implementation:
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
using (var identityManager = _identityManagerFactory.Create())
{
var user = await identityManager.FindAsync(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
ClaimsIdentity oAuthIdentity = await identityManager.CreateIdentityAsync(user, context.Options.AuthenticationType);
AuthenticationProperties properties = CreatePropertiesAsync(user);
AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
context.Validated(ticket);
}
}
I just had to set the provider for it to generate refresh tokens.
Any comments for pointers on when to set refresh tokens and not would be nice.
RefreshTokenProvider = new AuthenticationTokenProvider
{
OnCreate = CreateRefreshToken,
OnReceive = ReceiveRefreshToken,
}
private void CreateRefreshToken(AuthenticationTokenCreateContext context)
{
context.SetToken(context.SerializeTicket());
}
private void ReceiveRefreshToken(AuthenticationTokenReceiveContext context)
{
context.DeserializeTicket(context.Token);
}

Resources