I'd want to determine whether JWT Token is valid in [AllowAnonymouse]] endpoint
edit
I have an endpoint which can be accessed by anyone (authorized and unauthorized people) and then: If that user has http authorization header OR he has token in cookies and his token valid then redirect him to X otherwise to Y
Pseudocode of my idea:
[Route("Passport/")]
public IActionResult Passport()
{
if (this.User.Identity.IsAuthenticated)
or pseudocode:
if (tokenIsValid(getJWTTokenFromHeader()));
{
return RedirectToAction("Resources");
}
else
{
return RedirectToAction("Login");
}
}
I thought about something like this:
[Route("Passport/")]
public IActionResult Passport()
{
var token = ExtractTokenFromHeader();
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue
("application/json"));
client.DefaultRequestHeaders.Add("Authorization", $"Bearer {token}");
var get = client.GetAsync($"http://localhost/verifyToken").Result;
var responseBody = await get.Content.ReadAsStringAsync().ConfigureAwait(false);
switch (get.StatusCode)
{
case HttpStatusCode.Unauthorized:
return RedirectToAction("Login");
case HttpStatusCode.OK:
return RedirectToAction("Resources");
default:
return RedirectToAction(...);
}
}
Where endpoint verifyToken has an [Authorize] attribute and just returns Unauthorized (defaultly) or OK (from code)
You may try below code
private static bool ValidateJWTToken(string token, out string username) {
username = null;
var simplePrinciple = JwtManager.GetPrincipal(token);
var identity = simplePrinciple.Identity as ClaimsIdentity;
if (identity == null) return false;
if (!identity.IsAuthenticated) return false;
}
Related
This API is intended for a mobile application. The goal is to let the user confirm the email upon registration. When the user registers, a confirmation link is generated and sent over the email. I've done it the same way in a MVC project, it worked fine, but in a Web API project looks like it ain't gonna cut.
Now when the user clicks that link, the respective action method should be hit and do the job.
The only problem is, the ConfirmEmail action method is just not getting triggered when clicking the confirmation link although it looked fine.
Here are the main configurations which might help
MVC service configuration
services.AddMvc(options =>
{
options.EnableEndpointRouting = true;
options.Filters.Add<ValidationFilter>();
})
.AddFluentValidation(mvcConfiguration => mvcConfiguration.RegisterValidatorsFromAssemblyContaining<Startup>())
.SetCompatibilityVersion(Microsoft.AspNetCore.Mvc.CompatibilityVersion.Version_3_0);
Identity Service
public async Task<AuthenticationResult> RegisterAsync(string email, string password)
{
var existingUser = await _userManager.FindByEmailAsync(email);
if(existingUser != null)
{
return new AuthenticationResult { Errors = new[] { "User with this email address exists" } };
}
// generate user
var newUser = new AppUser
{
Email = email,
UserName = email
};
// register user in system
var result = await _userManager.CreateAsync(newUser, password);
if (!result.Succeeded)
{
return new AuthenticationResult
{
Errors = result.Errors.Select(x => x.Description)
};
}
// when registering user, assign him user role, also need to be added in the JWT!!!
await _userManager.AddToRoleAsync(newUser, "User");
// force user to confirm email, generate token
var token = await _userManager.GenerateEmailConfirmationTokenAsync(newUser);
// generate url
var confirmationLink = _urlHelper.Action("ConfirmEmail", "IdentityController",
new { userId = newUser.Id, token = token }, _httpRequest.HttpContext.Request.Scheme);
// send it per email
var mailresult =
await _emailService.SendEmail(newUser.Email, "BingoApp Email Confirmation",
$"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(confirmationLink)}'>clicking here</a>.");
if (mailresult)
return new AuthenticationResult { Success = true };
else
return new AuthenticationResult { Success = false, Errors = new List<string> { "Invalid Email Address"} };
}
Controller
[HttpPost(ApiRoutes.Identity.Register)]
public async Task<IActionResult> Register([FromBody] UserRegistrationRequest request)
{
if (!ModelState.IsValid)
{
return BadRequest(new AuthFailedResponse
{
Errors = ModelState.Values.SelectMany(x => x.Errors.Select(xx => xx.ErrorMessage))
});
}
// register the incoming user data with identity service
var authResponse = await _identityService.RegisterAsync(request.Email, request.Password);
if (!authResponse.Success)
{
return BadRequest(new AuthFailedResponse
{
Errors = authResponse.Errors
});
}
// confirm registration
return Ok();
}
[HttpGet]
public async Task<IActionResult> ConfirmEmail(string userId, string token)
{
if (userId == null || token == null)
{
return null;
}
var user = await _userManager.FindByIdAsync(userId);
if (user == null)
{
return null;
}
var result = await _userManager.ConfirmEmailAsync(user, token);
if (result.Succeeded)
{
await _emailService.SendEmail(user.Email, "BingoApp - Successfully Registered", "Congratulations,\n You have successfully activated your account!\n " +
"Welcome to the dark side.");
}
return null;
}
Your _urlHelper.Action(..) looks a bit suspicious to me.
I'm not sure you should pass the full controller name, that is, including the actual word controller.
Try _urlHelper.Action("ConfirmEmail", "Identity", instead.
As a tip: I try to avoid magic strings like these by using nameof(IdentityController) because it will return the controller name without the controller postfix.
following is code for calling web API call
public async System.Threading.Tasks.Task<ActionResult> RequisitionNameByQuantityThisDraw()
{
//Guid applicationRequisitionOid
string userName = string.Empty;
SessionObject sessionData = new SessionObject().GetSessionData();
if (sessionData == null)
{
return RedirectToAction("UserLogin", "Login");
}
Guid aa = new Guid("41CF8843-2AF4-40D0-9998-D6D516367A7D");
HttpResponseMessage response = _HttpClient.GetAsync("api/ApplicationSIRMeasure/RequisitionNameByQuantity?applicationRequisitionOid=" + aa).Result;
string userJsonString = await response.Content.ReadAsStringAsync();
return Json(userJsonString, JsonRequestBehavior.AllowGet);
}
below is web API methods
public HttpResponseMessage Get(Guid applicationRequisitionOid)
{
var result = _IService.GetAll(applicationRequisitionOid);
if (result == null)
return Request.CreateErrorResponse(HttpStatusCode.NotFound, "No data found");
else
return Request.CreateResponse(HttpStatusCode.OK, result);
}
[Route("api/ApplicationSIRMeasure/RequisitionNameByQuantity/{applicationRequisitionOid:Guid}")]
public HttpResponseMessage RequisitionNameByQuantity(Guid applicationRequisitionOid)
{
Guid id = new Guid("41CF8843-2AF4-40D0-9998-D6D516367A7D");
var result = _IService.GetRequisitionByQunatityThisDraw(id);
if (result == null)
return Request.CreateErrorResponse(HttpStatusCode.NotFound, "No data found");
else
return Request.CreateResponse(HttpStatusCode.OK, result);
}
whenever I call this web API with above code it goes to first get method.
but I won't to call in a second method
The route on the Web API is
[Route("api/ApplicationSIRMeasure/RequisitionNameByQuantity/{applicationRequisitionOid:guid}")]
Yet the request is calling
"api/ApplicationSIRMeasure/RequisitionNameByQuantity?applicationRequisitionOid="
Because the url does not match the second route template it is finding a match on the other action because the query string parameter matches.
you need to update the URL being called to match the route template of the target action.
var aa = new Guid("41CF8843-2AF4-40D0-9998-D6D516367A7D");
var url = string.Format("api/ApplicationSIRMeasure/RequisitionNameByQuantity/{0}", aa);
var response = await _HttpClient.GetAsync(url);
I really do not see the need to create the Guid when you can just build the URL with the same string that was used to create the Guid instance
I am setting up a multi tenant application and I am having issues creating a GraphServiceClient.
I have to following AuthorizationCodeReceived:
AuthorizationCodeReceived = async context =>
{
var tenantId =
context.AuthenticationTicket.Identity.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value;
var authenticationContext = new AuthenticationContext("https://login.microsoftonline.com/"+ tenantId);
await authenticationContext.AcquireTokenByAuthorizationCodeAsync(
context.Code,
new Uri("http://localhost:21925"),
new ClientCredential(ClientId, ClientSecret),
"https://graph.microsoft.com");
}
This works perfectly to authenticate the user. I am using fiddler, and I see that a new bearer token was given by login.microsoftonline.com/{tenantid}/oauth2/token
When creating a new Graph Service Client I use the following factory method:
public IGraphServiceClient CreateGraphServiceClient()
{
var client = new GraphServiceClient(
new DelegateAuthenticationProvider(
async requestMessage =>
{
string token;
var currentUserId = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
var currentUserHomeTenantId = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value;
var authenticationContext = new AuthenticationContext("https://login.microsoftonline.com/" + currentUserHomeTenantId + "/");
var clientCredential = new ClientCredential(_configuration.ClientId, _configuration.ClientSecret);
try
{
var authenticationResult = await authenticationContext.AcquireTokenSilentAsync(
GraphResourceId,
clientCredential,
new UserIdentifier(currentUserId, UserIdentifierType.UniqueId));
token = authenticationResult.AccessToken;
}
catch (AdalSilentTokenAcquisitionException e)
{
var result = await authenticationContext.AcquireTokenAsync(GraphResourceId, clientCredential);
token = result.AccessToken;
}
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", token);
}));
return client;
}
This method always throws an AdalSilentAcquisitionException and the AcquireTokenAsync retrieves a new token.
With this token, I am not able to request 'Me' on the graph.
I get the following exception: message=Resource 'some guid' does not exist or one of its queried reference-property objects are not present.
However, if I am debugging and I change the token before it is passed to the header, with the value of the one I got previously right after login in (received from login.microsoftonline.com/{tenantid}/oauth2/token ) then the API call works.
Does anyone know what I am doing wrong? He can I get the acquiretokensilently working?
Update: I have updated the code samples. I have removed the custom cache, and now everything seems to work.
How can I make a custom cache based on the http sessions, making sure the AcquireTokenSilently works.
Preview of not working token cache:
public class WebTokenCache : TokenCache
{
private readonly HttpContext _httpContext;
private readonly string _cacheKey;
public WebTokenCache()
{
_httpContext = HttpContext.Current;
var claimsPrincipal = (ClaimsPrincipal) HttpContext.Current.User;
_cacheKey = BuildCacheKey(claimsPrincipal);
AfterAccess = AfterAccessNotification;
LoadFromCache();
}
private string BuildCacheKey(ClaimsPrincipal claimsPrincipal)
{
var clientId = claimsPrincipal.FindFirst("aud").Value;
return $"{claimsPrincipal.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value}_TokenCache";
}
private void LoadFromCache()
{
var token = _httpContext.Cache[_cacheKey];
if (token == null) return;
Deserialize((byte[]) token);
}
private void AfterAccessNotification(TokenCacheNotificationArgs args)
{
if (!HasStateChanged) return;
if (Count > 0)
{
_httpContext.Cache[_cacheKey] = Serialize();
}
else
{
_httpContext.Cache.Remove(_cacheKey);
}
HasStateChanged = false;
}
}
I am trying use the code above and it works well form me.
Please ensure that the GraphResourceId is https://graph.microsoft.com(This resource is requested first time in your startUp class) since the method AcquireTokenSilentAsync will try to retrieve the token from cache based on the resrouce.
I'm trying to return an appropriate error message when a user tries to call the service when their authorization token has expired or if they have an invalid token.
The problem I'm having is the first time it is called, the message is sent properly, but after the first time the SendAsync method is called 4 times and the message data returns null.
I'm confused as to why it loops 4 times, and I tried stepping through it, but I can't get any further in the code.
Here is the code:
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
CancellationToken cancellationToken)
{
if (request.Headers != null)
{
// ....
if (request.Headers.GetValues(CustomTokenHeader).FirstOrDefault() == null)
{
//unauthorized response(401)
return FromResult(_unauthorizedResponse);
}
var authHeader = request.Headers.GetValues(CustomTokenHeader).FirstOrDefault();
if (String.IsNullOrWhiteSpace(authHeader))
{
//unauthorized response(401)
return FromResult(_unauthorizedResponse);
}
//authenticate token
return HandleTokenAuthentication(request, cancellationToken, authHeader);
}
}
static Task<T> FromResult<T>(T t)
{
var tcs = new TaskCompletionSource<T>();
tcs.SetResult(t);
return tcs.Task;
}
private Task<HttpResponseMessage> HandleTokenAuthentication(HttpRequestMessage request, CancellationToken cancellationToken, string authHeader)
{
//parse token
var token = ParseToken(authHeader);
if (String.IsNullOrWhiteSpace(token))
{
//unauthorized response(401)
return FromResult(_unauthorizedResponse);
}
//decrypt token
var tokenInfo = DecryptToken(token);
if (tokenInfo == null)
{
//unauthorized response(401)
return FromResult(_unauthorizedResponse);
}
//validate token
var claims = ValidateToken(tokenInfo, token);
if (claims == null)
{
//unauthorized response(401)
return FromResult(_unauthorizedTokenExpired);
}
var principal = CheckCustomAuthorization(claims);
if (principal == null)
{
//unauthorized response(401)
return FromResult(_unauthorizedResponse);
}
if (!principal.Identity.IsAuthenticated)
{
var loginFailureMessage = new HttpResponseMessage(HttpStatusCode.Unauthorized)
{
Content = new StringContent(((AgencyClaims)principal.Identity).LoginFailureReason)
};
return FromResult(loginFailureMessage);
}
//assign principal
Thread.CurrentPrincipal = principal;
return base.SendAsync(request, cancellationToken)
.ContinueWith(task => AuthorizedResponse(request, task.Result));
}
static HttpResponseMessage AuthorizedResponse(HttpRequestMessage request, HttpResponseMessage response)
{
if ((request.Method == HttpMethod.Get && response.StatusCode == HttpStatusCode.OK
&& !response.Headers.Contains(CustomTokenHeader))
|| (request.Method == HttpMethod.Post && response.StatusCode == HttpStatusCode.Created
&& !response.Headers.Contains(CustomTokenHeader)))
{
var token = ((AgencyClaims) Thread.CurrentPrincipal.Identity).Token;
response.Headers.Add(CustomTokenHeader, Convert.ToBase64String(Encoding.ASCII.GetBytes(token)));
}
return response;
}
readonly HttpResponseMessage _unauthorizedResponse =
new HttpResponseMessage(HttpStatusCode.Unauthorized) { Content = new StringContent("PROPER ERROR MESSAGE")};
Successful response:
<data contentType="text/plain; charset=utf-8" contentLength="21"><![CDATA[Authentication failed]]></data>
Here is the response after the first successful response:
<data contentType="null" contentLength="0"><![CDATA[]]></data>
Here is part of the request:
GET http://localhost:20559/api/Service?Name=Jack HTTP/1.1
Ok, I was able to resolve the issue. The _unauthorizedResponse class variable is somehow allowing the code to run once through successfully, but not a second time. This issue is not related to the readonly modifier, since it still doesn't work without it. I'm not sure how this works (and maybe someone on here can explain), but by moving them into the local scope within the method it is able to run correctly each time.
I'm using OpenId on a new website and am trying to get some basic information about the user, see the code below. Why is the following allways null?
var myData = response.GetExtension<ClaimsResponse>();
And the main code
[System.Web.Mvc.AcceptVerbs(HttpVerbs.Get)]
public ActionResult LogOn()
{
var openid = new OpenIdRelyingParty();
IAuthenticationResponse response = openid.GetResponse();
if (response != null)
{
switch (response.Status)
{
case AuthenticationStatus.Authenticated:
FormsAuthentication.RedirectFromLoginPage(
response.ClaimedIdentifier, false);
var myData = response.GetExtension<ClaimsResponse>();
break;
case AuthenticationStatus.Canceled:
ModelState.AddModelError("loginIdentifier",
"Login was cancelled at the provider");
break;
case AuthenticationStatus.Failed:
ModelState.AddModelError("loginIdentifier",
"Login failed using the provided OpenID identifier");
break;
}
}
return View("Register");
}
[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
{
Email = DemandLevel.Request,
FullName = DemandLevel.Request
});
return request.RedirectingResponse.AsActionResult();
}
}
http://www.dotnetopenauth.net/developers/help/the-axfetchassregtransform-behavior/