Setting authorization header of Httpclient return internal server error 500 repone why? - asp.net

I have a problem I don't know where , after setting authorization header of HttpClient return internal server error 500.
Auth Controller Login Action
namespace MagicVilla_Web.Controllers
{
public class AuthController : Controller
{
private readonly IAuthService _authService;
public AuthController(IAuthService authService)
{
_authService = authService;
}
[HttpGet]
public IActionResult Login()
{
LoginRequestDTO loginRequestDTO = new LoginRequestDTO();
return View(loginRequestDTO);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginRequestDTO loginRequestDTO)
{
APIResponse response = await _authService.LoginAsync<APIResponse>(loginRequestDTO);
if (response != null && response.IsSuccess)
{
LoginResponseDTO model = JsonConvert.DeserializeObject<LoginResponseDTO (Convert.ToString(response.Result));
var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme);
identity.AddClaim(new Claim(ClaimTypes.Name, model.User.Name));
identity.AddClaim(new Claim(ClaimTypes.Role, model.User.Role));
var principal = new ClaimsPrincipal(identity);
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal);
HttpContext.Session.SetString(SD.SessionToken, model.Token);
return RedirectToAction("Index","Home");
}
else
{
ModelState.AddModelError("CustomError", response.ErrorMessages.FirstOrDefault());
return View(loginRequestDTO);
}
}
[HttpGet]
public IActionResult Register()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Register(RegisterationRequestDTO registerationRequestDTO)
{
APIResponse result = await _authService.RegisterAsync<APIResponse>(registerationRequestDTO);
if (result != null && result.IsSuccess)
{
return RedirectToAction("Login");
}
return View();
}
[HttpGet]
public async Task<IActionResult> Logout()
{
await HttpContext.SignOutAsync();
HttpContext.Session.SetString(SD.SessionToken, "");
return RedirectToAction("Index", "Home");
}
[HttpGet]
public async Task<IActionResult> AccessDenied()
{
return View();
}
}
}
Home Controller Index
namespace MagicVilla_Web.Controllers
{
public class HomeController : Controller
{
private readonly IVillaService villaService;
private readonly IMapper mapper;
public HomeController(IVillaService villaService, IMapper mapper)
{
this.villaService = villaService;
this.mapper = mapper;
}
public async Task<IActionResult> Index()
{
List<VillaDTO> list = new();
var response = await villaService.GetAllAsync<APIResponse>(HttpContext.Session.GetString(SD.SessionToken));
if (response != null && response.IsSuccess)
{
list = JsonConvert.DeserializeObject<List<VillaDTO>>(Convert.ToString(response.Result));
}
return View(list);
}
}
}
GetAllAsync From VillaService.cs
public class VillaService : BaseService , IVillaService
{
private readonly IHttpClientFactory clientFactory;
private string villaUrl;
public VillaService(IHttpClientFactory clientFactory, IConfiguration configuration) : base(clientFactory)
{
this.clientFactory = clientFactory;
this.villaUrl = configuration.GetValue<string>("ServiceUrls:VillaAPI");
}
public Task<T> GetAllAsync<T>(string token)
{
return SendAsync<T>(new APIRequest
{
ApiType = SD.ApiType.GET,
Url = this.villaUrl + "/api/VillaAPI",
Token = token
});
}
}
Send Async From BaseService.cs
Here response return internal server error 500 after pass the token in the header Authrization & not going to API endpoint .
namespace MagicVilla_Web.Services
{
public class BaseService : IBaseServices
{
public APIResponse APIResponse { get; set; }
public IHttpClientFactory httpClient { get; set; }
public BaseService(IHttpClientFactory httpClient)
{
this.APIResponse = new();
this.httpClient = httpClient;
}
public async Task<T> SendAsync<T>(APIRequest apiRequest)
{
try
{
var client = httpClient.CreateClient("MagicAPI");
HttpRequestMessage request = new HttpRequestMessage();
request.Headers.Add("Accept", "application/json");
request.RequestUri = new Uri(apiRequest.Url);
if (apiRequest.Data != null)
{
request.Content = new StringContent(JsonConvert.SerializeObject(apiRequest.Data), Encoding.UTF8, "application/json");
}
switch (apiRequest.ApiType)
{
case SD.ApiType.POST:
request.Method = HttpMethod.Post;
break;
case SD.ApiType.PUT:
request.Method = HttpMethod.Put;
break;
case SD.ApiType.DELETE:
request.Method = HttpMethod.Delete;
break;
default:
request.Method = HttpMethod.Get;
break;
}
HttpResponseMessage response = null;
if (!string.IsNullOrEmpty(apiRequest.Token))
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", apiRequest.Token);
}
response = await client.SendAsync(request);
var apiContent = await response.Content.ReadAsStringAsync();
try
{
APIResponse apiResponse = JsonConvert.DeserializeObject<APIResponse>(apiContent);
if ((response.StatusCode == System.Net.HttpStatusCode.BadRequest || response.StatusCode == System.Net.HttpStatusCode.NotFound))
{
apiResponse.StatusCode = System.Net.HttpStatusCode.BadRequest;
apiResponse.IsSuccess = false;
var apiResponseSerialized = JsonConvert.SerializeObject(apiResponse);
var apiResponseDeserialized = JsonConvert.DeserializeObject<T>(apiResponseSerialized);
return apiResponseDeserialized;
}
}
catch (Exception e)
{
var exceptionResponse = JsonConvert.DeserializeObject<T>(apiContent);
return exceptionResponse;
}
var APIResponse = JsonConvert.DeserializeObject<T>(apiContent);
return APIResponse;
}
catch (Exception e)
{
var dto = new APIResponse
{
ErrorMessages = new List<string> { Convert.ToString(e.Message) },
IsSuccess = false
};
var res = JsonConvert.SerializeObject(dto);
var apiResponse = JsonConvert.DeserializeObject<T>(res);
return apiResponse;
}
}
}
}
VillaAPI Controller
[Route("api/[controller]")]
[ApiController]
public class VillaAPIController : ControllerBase
{
public readonly IVillaRepository villaRepository;
public readonly IMapper mapper;
protected APIResponse response;
public VillaAPIController(IVillaRepository villaRepository, IMapper mapper)
{
this.villaRepository = villaRepository;
this.mapper = mapper;
this.response = new();
}
[HttpGet]
[Authorize]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<IEnumerable<APIResponse>>> GetVillas()
{
try
{
IEnumerable<Villa> villaList = await villaRepository.GetAllAsync();
response.Result = mapper.Map<List<VillaDTO>>(villaList);
response.StatusCode = System.Net.HttpStatusCode.OK;
}
catch (Exception ex)
{
response.IsSuccess = false;
response.ErrorMessages = new List<string> { ex.Message };
}
return Ok(response);
}
}
Program.cs API
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddDbContext<ApplicationDbContext>(o => {
o.UseSqlServer(builder.Configuration.GetConnectionString("DefaultSQLConnection"));
});
builder.Services.AddAutoMapper(typeof(MappingConfig));
builder.Services.AddScoped<IVillaRepository, VillaRepository>();
builder.Services.AddScoped<IVillaNumberRepository, VillaNumberRepository>();
builder.Services.AddScoped<IUserRepository, UserRepository>();
builder.Services.AddControllers(o => {
//o.ReturnHttpNotAcceptable = true;
}).AddNewtonsoftJson().AddXmlDataContractSerializerFormatters();
builder.Services.AddEndpointsApiExplorer();
//get secret key from appSittings.json
var key = builder.Configuration.GetValue<string>("ApiSettings:Secret");
//add Authentication Configurations
builder.Services.AddAuthentication(x =>
{
//configure the default authentication scheme and the default challenge scheme. Both of them we want to use JWT bearer defaults
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
//we have something called as add JWT bearer and then we can configure options on that.
.AddJwtBearer(x =>
{
x.RequireHttpsMetadata = false; //we have the required https metadata
x.SaveToken = true; //save the token
x.Authority = "https://localhost:7003/";
x.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = false,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(key)),
ValidateIssuer = false,
ValidateAudience = false,
ValidateLifetime = false,
};
});
//to enable bearer in swagger
builder.Services.AddSwaggerGen(options =>
{
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Description =
"JWT Authorization header using the Bearer scheme. \r\n\r\n " +
"Enter 'Bearer' [space] and then your token in the text input below.\r\n\r\n" +
"Example: \"Bearer 12345abcdef\"",
Name = "Authorization",
In = ParameterLocation.Header,
Scheme = "Bearer"
});
options.AddSecurityRequirement(new OpenApiSecurityRequirement()
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
},
Scheme = "oauth2",
Name = "Bearer",
In = ParameterLocation.Header
},
new List<string>()
}
});
});
//app => configured application
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
Program.cs Web
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllersWithViews();
builder.Services.AddAutoMapper(typeof(MappingConfig));
builder.Services.AddHttpClient<IVillaService, VillaService>();
builder.Services.AddScoped<IVillaService, VillaService>();
builder.Services.AddHttpClient<IVillaNumberService, VillaNumberService>();
builder.Services.AddScoped<IVillaNumberService, VillaNumberService>();
builder.Services.AddHttpClient<IAuthService, AuthService>();
builder.Services.AddScoped<IAuthService, AuthService>();
builder.Services.AddDistributedMemoryCache();
IdentityModelEventSource.ShowPII = true;
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.Cookie.HttpOnly = true;
options.ExpireTimeSpan = TimeSpan.FromMinutes(30);
options.SlidingExpiration = true;
options.LoginPath = "/Auth/Login";
options.AccessDeniedPath = "/Auth/AccessDenied";
});
//add authorization
//builder.Services.AddAuthorization();
builder.Services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromMinutes(100);
options.Cookie.HttpOnly = true;
options.Cookie.IsEssential = true;
});
builder.Services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseSession();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
If I turn off the authorization and not send the token in the header, everything works fine .MagicVilla_VillaAPI this project link in Github :https://github.com/MoZytoon/MagicVilla

The problem was with the program.cs file for the Api. here :
x. Authority = "https://localhost:7003/";
When setting the the AddAuthentication .

Related

How to do FileUpload in ASP.NET Core 6 and return file Url

I am going to save image local file and return image url:
namespace img_cleaner.Controllers;
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
public HomeController(ILogger<HomeController> logger)
{
_logger = logger;
}
public IActionResult Index()
{
return View();
}
public IActionResult Privacy()
{
return View();
}
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
[HttpPost]
public async ValueTask<IActionResult> ImageReiceve(ImageUpload imageUpload)
{
MultipartFormDataContent form=new MultipartFormDataContent();
HttpClient client=new HttpClient();
ArgumentNullException.ThrowIfNull(imageUpload.file);
var fileStream = imageUpload.file?.OpenReadStream();
StreamContent content = new StreamContent(fileStream);
content.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data")
{
Name = "file",
FileName = imageUpload.file?.FileName
};
form.Add(content);
var response = await client.PostAsync("http://localhost:5000/",form);
var imgResult = await response.Content.ReadAsByteArrayAsync();
var imgResultBase64 = Convert.ToBase64String(imgResult);
return View(new ImageResult(){ ImageStringbase64 = imgResultBase64 });
}
};
Program.cs:
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllersWithViews();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseStaticFiles();
app.UseHttpsRedirection();
app.UseRouting();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();

How to fix 'Unauthorized Error 401', even when logged in?

I'm creating this webpage in Angular with .NET as my backend. Everything was working just fine, but today (all of a sudden) I'm getting Unauthorized errors (401). When I remove my authorization, I'm getting internal server errors (meaning my 'User.Identity.Name') does not work. So I'm guessing there is a problem with my JWT
I have tried to remove authorization headers. But that gave me an internal server error (500). The only requests that work are those not using IdentityUser and thus being [AllowAnonymous]
I have no idea what the problem could be, so it's hard to show some code. But since I think the problem has something to do with my JWT I'll add some code of that.
Some controller code .NET
[Route("api/[controller]")]
[ApiController]
[Produces("application/json")]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public class BierenController : ControllerBase
{
private readonly IBierRepository _bierRepository;
private readonly IGebruikerRepository _gebruikerRepository;
private readonly IBrouwerRepository _brouwerRepository;
public BierenController(IBierRepository bierRepository, IGebruikerRepository gebruikerRepository, IBrouwerRepository brouwerRepository)
{
_bierRepository = bierRepository;
_gebruikerRepository = gebruikerRepository;
_brouwerRepository = brouwerRepository;
}
[HttpGet]
[AllowAnonymous]
public IEnumerable<Bier> GetBieren()
{
return _bierRepository.GetAll().OrderBy(b => b.Naam);
}
[HttpGet("{id}")]
[AllowAnonymous]
public ActionResult<Bier> GetBier(int id)
{
Bier bier = _bierRepository.GetBy(id);
if (bier == null)
return NotFound();
return bier;
}
[HttpPost]
[Authorize(Roles = "Brouwer, Admin")]
public IActionResult PostBier(Bier bier)
{
Brouwer brouwer = _brouwerRepository.GetBy(User.Identity.Name);
brouwer.AddBier(bier);
_brouwerRepository.SaveChanges();
return NoContent();
}
Startup.cs configuration
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddDbContext<BrouwerContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("BierContext")));
services.AddScoped<IBrouwerRepository, BrouwerRepository>();
services.AddScoped<IBierRepository, BierRepository>();
services.AddScoped<IGebruikerRepository, GebruikerRepository>();
services.AddScoped<IPostRepository, PostRepository>();
services.AddOpenApiDocument(c =>
{
c.Title = "BabAPI";
c.Version = "v1";
c.Description = "De API voor BAB";
c.DocumentProcessors.Add(new SecurityDefinitionAppender("JWT Token", new SwaggerSecurityScheme
{
Type = SwaggerSecuritySchemeType.ApiKey,
Name = "Authorization",
In = SwaggerSecurityApiKeyLocation.Header,
Description = "Copy 'Bearer' + valid JWT token into field" }));
c.OperationProcessors.Add(new OperationSecurityScopeProcessor("JWT Token"));
});
services.AddCors(options =>
options.AddPolicy("AllowAllOrigins", builder =>
builder.AllowAnyOrigin()
));
services.AddDefaultIdentity<IdentityUser>(cfg => cfg.User.RequireUniqueEmail = true).AddRoles<IdentityRole>().AddEntityFrameworkStores<BrouwerContext>();
services.Configure<IdentityOptions>(options =>
{
// Password settings.
options.Password.RequireDigit = true;
options.Password.RequiredLength = 6;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequiredUniqueChars = 1;
// Lockout settings.
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
options.Lockout.MaxFailedAccessAttempts = 5;
options.Lockout.AllowedForNewUsers = true;
// User settings.
options.User.AllowedUserNameCharacters =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
options.User.RequireUniqueEmail = true;
});
services.AddAuthentication(x => { x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; }).AddJwtBearer(x =>
{
x.RequireHttpsMetadata = false; x.SaveToken = true; x.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Tokens:Key"])),
ValidateIssuer = false,
ValidateAudience = false,
RequireExpirationTime = true
};
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IServiceProvider services)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseStaticFiles();
app.UseHttpsRedirection();
app.UseMvc();
CreateUserRoles(services).Wait();
app.UseAuthentication();
app.UseSwaggerUi3();
app.UseSwagger();
app.UseCors("AllowAllOrigins");
}
Angular authentication interceptor
export class AuthenticationInterceptor implements HttpInterceptor {
constructor(private authService: AuthenticationService) { }
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (this.authService.token.length) {
const clonedRequest = req.clone({
headers: req.headers.set('Authorization', `Bearer ${this.authService.token}`)
});
return next.handle(clonedRequest);
}
return next.handle(req);
}
}
.NET Login/Register/Tokencreation
[AllowAnonymous]
[HttpPost]
public async Task<ActionResult<String>> CreateToken(LoginDTO model)
{
var user = await this._userManager.FindByNameAsync(model.GebruikersNaam);
if(user == null)
{
user = await this._userManager.FindByEmailAsync(model.GebruikersNaam);
}
if (user != null)
{
var result = await _signInManager.CheckPasswordSignInAsync(user, model.Password, false);
if (result.Succeeded)
{
string token = await GetTokenAsync(user);
return Created("", token);
}
}
return BadRequest();
}
private async Task<string> GetTokenAsync(IdentityUser user)
{
var role = await _userManager.GetRolesAsync(user);
IdentityOptions _options = new IdentityOptions();
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Tokens:Key"]));
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[] {
new Claim(JwtRegisteredClaimNames.UniqueName, user.UserName),
new Claim(_options.ClaimsIdentity.RoleClaimType, role.FirstOrDefault())
}),
SigningCredentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256),
Expires = DateTime.Now.AddMinutes(30)
};
var tokenHandler = new JwtSecurityTokenHandler();
var securityToken = tokenHandler.CreateToken(tokenDescriptor);
var token = tokenHandler.WriteToken(securityToken);
return token;
}
[AllowAnonymous]
[HttpPost("register")]
public async Task<ActionResult<String>> Register(RegisterDTO model)
{
IdentityUser user = new IdentityUser { UserName = model.GebruikersNaam, Email = model.Email };
var result = await _userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
if (model.Email.ToLower().Equals("admin1#hotmail.com"))
{
Brouwer brouwer = new Brouwer { Naam = model.GebruikersNaam, Email = model.Email };
_brouwerRepository.Add(brouwer);
_brouwerRepository.SaveChanges();
await _userManager.AddToRoleAsync(user, "Admin");
}
else if (model.Email.ToLower().Equals("brouwer1#hotmail.com"))
{
Brouwer brouwer = new Brouwer { Naam = model.GebruikersNaam, Email = model.Email };
_brouwerRepository.Add(brouwer);
_brouwerRepository.SaveChanges();
await _userManager.AddToRoleAsync(user, "Brouwer");
}
else
{
Gebruiker gebruiker = new Gebruiker { Email = model.Email, Gebruikersnaam = model.GebruikersNaam };
_gebruikerRepository.Add(gebruiker);
_gebruikerRepository.SaveChanges();
await _userManager.AddToRoleAsync(user, "User");
}
string token = await GetTokenAsync(user);
return Created("", token);
}
else {
return BadRequest();
}

Authentication and LoginPath for different areas in ASP.NET Core 2

ASP.NET Core 2
Help me to configure AddAuthentication for two routes: users (user accounts) and admin area.
For example, if user doesn't signed in and trying to enter /Account/Orders/ he'll be redirected to /Account/SignIn/.
But if someone trying access /Admin/Orders/ must be redireted to /Admin/Signin/
Have not found ay solution ATM.
Solved!
In admin area (controllers) we using Authorize attr. arg.: [Authorize(AuthenticationSchemes = "backend")] and that is.
BTW we are able to make any tuning by accessing HttpContext in AddCookie's options and events.
Configuration:
services
.AddAuthentication(o =>
{
o.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, o =>
{
o.LoginPath = new PathString("/account/login/");
})
.AddCookie("backend", o =>
{
o.LoginPath = new PathString("/admin/account/login/");
});
#Alex's answer got me 90% of the way there.
In .Net 6, I'm using this https://learn.microsoft.com/en-us/aspnet/core/security/authentication/cookie?view=aspnetcore-6.0 approach to use Cookies without using the Username setup in identity.
Program.cs
var authentication = services.AddAuthentication(o =>
{
o.DefaultScheme = AuthenticationSchemes.FrontEnd;
});
authentication.AddCookie(AuthenticationSchemes.FrontEnd, o =>
{
o.LoginPath = CookieAuthenticationDefaults.LoginPath;
});
authentication.AddCookie(AuthenticationSchemes.BackEnd, o =>
{
o.LoginPath = new PathString("/admin/login/");
o.AccessDeniedPath = new PathString("/admin/accessdenied");
});
AppAuthenticationSchemes.cs
public class AuthenticationSchemes
{
public const string FrontEnd = "Frontend";
public const string BackEnd = "Backend";
public const string Either = FrontEnd + "," + BackEnd;
}
AccountController.cs
[AllowAnonymous]
public class AccountController : Controller
{
private readonly FrontEndSecurityManager _frontEndSecurityManager;
public AccountController(FrontEndSecurityManager frontEndSecurityManager)
{
_frontEndSecurityManager = frontEndSecurityManager;
}
[HttpPost(Name = "Login")]
public async Task<ActionResult> Login(LoginViewModel loginModel)
{
if (string.IsNullOrEmpty(loginModel.Username) ||
string.IsNullOrEmpty(loginModel.Password))
{
ModelState.AddModelError("form", "Please enter Username and Password");
return RedirectToAction("Login", "Account");
}
var loginResult = await _frontEndSecurityManager.ValidateCredentials(loginModel.Username, loginModel.Password);
if (!loginResult.IsSuccess)
{
this.AddFlash(FlashMessageType.Danger, "UserName or Password is incorrect");
return RedirectToAction("Login", "Account");
}
var identity = await _frontEndSecurityManager.CreateIdentityAsync(loginModel.Username, loginResult);
await _frontEndSecurityManager.SignInAsync(identity, HttpContext);
return RedirectToAction("Menu", "App");
}
}
FrontEndSecurityManager.cs
public class FrontEndSecurityManager
{
private readonly SignInApi _api;
private readonly AuthenticationOptions _authenticationOptions;
public FrontEndSecurityManager(SignInApi api, IOptions<AuthenticationOptions> authenticationOptions)
{
_api = api;
_authenticationOptions = authenticationOptions.Value;
}
public async Task SignInAsync(ClaimsIdentity identity, HttpContext httpContext)
{
var authProperties = new AuthenticationProperties
{
AllowRefresh = true,
ExpiresUtc = DateTimeOffset.UtcNow.AddMinutes(30),
IsPersistent = true,
IssuedUtc = DateTimeOffset.UtcNow
};
await httpContext.SignOutAsync(AuthenticationSchemes.BackEnd);
await httpContext.SignInAsync(AuthenticationSchemes.FrontEnd, new ClaimsPrincipal(identity), authProperties);
}
public async Task<LoginResult> ValidateCredentials(string username, string password)
{
if (_authenticationOptions.DemoUserEnabled)
{
if (string.Equals(username, "demo", StringComparison.InvariantCultureIgnoreCase))
{
var result = new LoginResult(StandardResults.SuccessResult, "")
{
Employee_Name = "Demo User",
Employee_Email = "DemoGuy#gmail.com",
Employee_Initials = "DG",
Employee_Type = "Regular",
Role1 = true,
Role2 = true,
Role3 = false
};
return result;
}
}
var apiRequest = new LoginRequest() { Username = username, Password = password };
var loginResult = await _api.LoginAsync(apiRequest);
if (loginResult.Success)
{
return loginResult.Data;
}
else
{
return LoginResult.Failure();
}
}
public async Task<ClaimsIdentity> CreateIdentityAsync(string username, LoginResult loginResult)
{
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, username),
new Claim(ClaimTypes.Role, AppRoles.User),
new Claim(ClaimTypes.Email, loginResult.Employee_Email, ClaimValueTypes.Email),
new Claim(ClaimTypes.GivenName, loginResult.GivenName),
new Claim(ClaimTypes.Surname, loginResult.Surname),
new Claim(AppClaimTypes.EmployeeType, loginResult.Employee_Type),
new Claim(AppClaimTypes.EmployeeInitials, loginResult.Employee_Initials),
new Claim(AppClaimTypes.Location, loginResult.Location.ToString(), ClaimValueTypes.Integer),
};
if (loginResult.Use_Checkin)
{
claims.Add(new Claim(ClaimTypes.Role, AppRoles.Checkin));
}
if (loginResult.Use_Pickup)
{
claims.Add(new Claim(ClaimTypes.Role, AppRoles.Pickup));
}
var identity = new ClaimsIdentity(claims, AuthenticationSchemes.FrontEnd);
return identity;
}
public void SignOutAsync(HttpContext httpContext)
{
httpContext.SignOutAsync(AuthenticationSchemes.FrontEnd);
}
}
From here, you could easily extrapolate how you want the back-end authentication controller to work. Essentially something like
await HttpContext.SignOutAsync(AuthenticationSchemes.FrontEnd);await HttpContext.SignInAsync(AuthenticationSchemes.BackEnd, new ClaimsPrincipal(identity), authProperties);
An example of using each policy would be
[Authorize(AuthenticationSchemes = AuthenticationSchemes.BackEnd)]
public IActionResult Secure()
{
return View("Secure");
}
or
[Authorize] // scheme not explicit, so Pr DefaultScheme = AuthenticationSchemes.FrontEnd is used
[Route("[controller]")]
public class OutgoingController : Controller
{
}

External Authentication ASP.NET Web API

I have Web API and the client that calls the web API in separate solutions. I need to authenticate users using local authentication with existing user data and also authenticate with external authentication like Google and Facebook.
Local Authentication works fine. But while authenticating external logins, I'm getting unauthorized response when calling api/Account/UserInfo. Please note that I'm able to generate the token from google account correctly.
Here's my AccountController.cs file.
[Authorize]
[RoutePrefix("api/Account")]
[EnableCors("*","*","*")]
public class AccountController : ApiController
{
private const string LocalLoginProvider = "Local";
private ApplicationUserManager _userManager;
public AccountController()
{
}
public AccountController(ApplicationUserManager userManager,
ISecureDataFormat<AuthenticationTicket> accessTokenFormat)
{
UserManager = userManager;
AccessTokenFormat = accessTokenFormat;
}
public ApplicationUserManager UserManager
{
get
{
return _userManager ?? Request.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
private set
{
_userManager = value;
}
}
public ISecureDataFormat<AuthenticationTicket> AccessTokenFormat { get; private set; }
// GET api/Account/UserInfo
[HostAuthentication(DefaultAuthenticationTypes.ExternalBearer)]
//[AllowAnonymous]
[Route("UserInfo")]
public UserInfoViewModel GetUserInfo()
{
ExternalLoginData externalLogin = ExternalLoginData.FromIdentity(User.Identity as ClaimsIdentity);
return new UserInfoViewModel
{
Email = User.Identity.GetUserName(),
HasRegistered = externalLogin == null,
LoginProvider = externalLogin != null ? externalLogin.LoginProvider : null
};
}
// POST api/Account/Logout
[Route("Logout")]
public IHttpActionResult Logout()
{
Authentication.SignOut(CookieAuthenticationDefaults.AuthenticationType);
return Ok();
}
// GET api/Account/ManageInfo?returnUrl=%2F&generateState=true
[Route("ManageInfo")]
public async Task<ManageInfoViewModel> GetManageInfo(string returnUrl, bool generateState = false)
{
IdentityUser user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
if (user == null)
{
return null;
}
List<UserLoginInfoViewModel> logins = new List<UserLoginInfoViewModel>();
foreach (IdentityUserLogin linkedAccount in user.Logins)
{
logins.Add(new UserLoginInfoViewModel
{
LoginProvider = linkedAccount.LoginProvider,
ProviderKey = linkedAccount.ProviderKey
});
}
if (user.PasswordHash != null)
{
logins.Add(new UserLoginInfoViewModel
{
LoginProvider = LocalLoginProvider,
ProviderKey = user.UserName,
});
}
return new ManageInfoViewModel
{
LocalLoginProvider = LocalLoginProvider,
Email = user.UserName,
Logins = logins,
ExternalLoginProviders = GetExternalLogins(returnUrl, generateState)
};
}
// POST api/Account/ChangePassword
[Route("ChangePassword")]
public async Task<IHttpActionResult> ChangePassword(ChangePasswordBindingModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
IdentityResult result = await UserManager.ChangePasswordAsync(User.Identity.GetUserId(), model.OldPassword,
model.NewPassword);
if (!result.Succeeded)
{
return GetErrorResult(result);
}
return Ok();
}
// POST api/Account/SetPassword
[Route("SetPassword")]
public async Task<IHttpActionResult> SetPassword(SetPasswordBindingModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
IdentityResult result = await UserManager.AddPasswordAsync(User.Identity.GetUserId(), model.NewPassword);
if (!result.Succeeded)
{
return GetErrorResult(result);
}
return Ok();
}
// POST api/Account/AddExternalLogin
[Route("AddExternalLogin")]
public async Task<IHttpActionResult> AddExternalLogin(AddExternalLoginBindingModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
Authentication.SignOut(DefaultAuthenticationTypes.ExternalCookie);
AuthenticationTicket ticket = AccessTokenFormat.Unprotect(model.ExternalAccessToken);
if (ticket == null || ticket.Identity == null || (ticket.Properties != null
&& ticket.Properties.ExpiresUtc.HasValue
&& ticket.Properties.ExpiresUtc.Value < DateTimeOffset.UtcNow))
{
return BadRequest("External login failure.");
}
ExternalLoginData externalData = ExternalLoginData.FromIdentity(ticket.Identity);
if (externalData == null)
{
return BadRequest("The external login is already associated with an account.");
}
IdentityResult result = await UserManager.AddLoginAsync(User.Identity.GetUserId(),
new UserLoginInfo(externalData.LoginProvider, externalData.ProviderKey));
if (!result.Succeeded)
{
return GetErrorResult(result);
}
return Ok();
}
// POST api/Account/RemoveLogin
[Route("RemoveLogin")]
public async Task<IHttpActionResult> RemoveLogin(RemoveLoginBindingModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
IdentityResult result;
if (model.LoginProvider == LocalLoginProvider)
{
result = await UserManager.RemovePasswordAsync(User.Identity.GetUserId());
}
else
{
result = await UserManager.RemoveLoginAsync(User.Identity.GetUserId(),
new UserLoginInfo(model.LoginProvider, model.ProviderKey));
}
if (!result.Succeeded)
{
return GetErrorResult(result);
}
return Ok();
}
// GET api/Account/ExternalLogin
[OverrideAuthentication]
[HostAuthentication(DefaultAuthenticationTypes.ExternalCookie)]
[HostAuthentication(DefaultAuthenticationTypes.ExternalBearer)]
[AllowAnonymous]
[Route("ExternalLogin", Name = "ExternalLogin")]
public async Task<IHttpActionResult> GetExternalLogin(string provider, string error = null)
{
if (error != null)
{
return Redirect(Url.Content("~/") + "#error=" + Uri.EscapeDataString(error));
}
if (!User.Identity.IsAuthenticated)
{
return new ChallengeResult(provider, this);
}
ExternalLoginData externalLogin = ExternalLoginData.FromIdentity(User.Identity as ClaimsIdentity);
if (externalLogin == null)
{
return InternalServerError();
}
if (externalLogin.LoginProvider != provider)
{
Authentication.SignOut(DefaultAuthenticationTypes.ExternalCookie);
return new ChallengeResult(provider, this);
}
ApplicationUser user = await UserManager.FindAsync(new UserLoginInfo(externalLogin.LoginProvider,
externalLogin.ProviderKey));
bool hasRegistered = user != null;
if (hasRegistered)
{
Authentication.SignOut(DefaultAuthenticationTypes.ExternalCookie);
ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(UserManager,
OAuthDefaults.AuthenticationType);
ClaimsIdentity cookieIdentity = await user.GenerateUserIdentityAsync(UserManager,
CookieAuthenticationDefaults.AuthenticationType);
AuthenticationProperties properties = ApplicationOAuthProvider.CreateProperties(user.UserName);
Authentication.SignIn(properties, oAuthIdentity, cookieIdentity);
}
else
{
IEnumerable<Claim> claims = externalLogin.GetClaims();
ClaimsIdentity identity = new ClaimsIdentity(claims, OAuthDefaults.AuthenticationType);
Authentication.SignIn(identity);
}
return Ok();
}
// GET api/Account/ExternalLogins?returnUrl=%2F&generateState=true
[AllowAnonymous]
[Route("ExternalLogins")]
public IEnumerable<ExternalLoginViewModel> GetExternalLogins(string returnUrl, bool generateState = false)
{
IEnumerable<AuthenticationDescription> descriptions = Authentication.GetExternalAuthenticationTypes();
List<ExternalLoginViewModel> logins = new List<ExternalLoginViewModel>();
string state;
if (generateState)
{
const int strengthInBits = 256;
state = RandomOAuthStateGenerator.Generate(strengthInBits);
}
else
{
state = null;
}
foreach (AuthenticationDescription description in descriptions)
{
ExternalLoginViewModel login = new ExternalLoginViewModel
{
Name = description.Caption,
Url = Url.Route("ExternalLogin", new
{
provider = description.AuthenticationType,
response_type = "token",
client_id = Startup.PublicClientId,
redirect_uri = new Uri(Request.RequestUri, returnUrl).AbsoluteUri,
state = state
}),
State = state
};
logins.Add(login);
}
return logins;
}
// POST api/Account/Register
[AllowAnonymous]
[Route("Register")]
public async Task<IHttpActionResult> Register(RegisterBindingModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var user = new ApplicationUser()
{
UserName = model.Email,
Email = model.Email,
FirstName = model.FirstName,
LastName = model.LastName,
CellNumber = model.CellNumber,
CompanyName = model.CompanyName,
Address = model.Address,
City = model.City,
Country = model.Country,
State = model.State,
Zip = model.Zip,
EmailConfirmed = true
};
IdentityResult result = await UserManager.CreateAsync(user, model.Password);
if (!result.Succeeded)
{
return GetErrorResult(result);
}
return Ok("User Registered Successfully!");
}
// POST api/Account/RegisterExternal
[OverrideAuthentication]
//[AllowAnonymous]
[HostAuthentication(DefaultAuthenticationTypes.ExternalBearer)]
[Route("RegisterExternal")]
public async Task<IHttpActionResult> RegisterExternal()
{
var info = await Authentication.GetExternalLoginInfoAsync();
if (info == null)
{
return InternalServerError();
}
var user = new ApplicationUser() { UserName = info.Email, Email = info.Email };
IdentityResult result = await UserManager.CreateAsync(user);
if (!result.Succeeded)
{
return GetErrorResult(result);
}
result = await UserManager.AddLoginAsync(user.Id, info.Login);
if (!result.Succeeded)
{
return GetErrorResult(result);
}
return Ok();
}
protected override void Dispose(bool disposing)
{
if (disposing && _userManager != null)
{
_userManager.Dispose();
_userManager = null;
}
base.Dispose(disposing);
}
#region Helpers
private IAuthenticationManager Authentication
{
get { return Request.GetOwinContext().Authentication; }
}
private IHttpActionResult GetErrorResult(IdentityResult result)
{
if (result == null)
{
return InternalServerError();
}
if (!result.Succeeded)
{
if (result.Errors != null)
{
foreach (string error in result.Errors)
{
ModelState.AddModelError("", error);
}
}
if (ModelState.IsValid)
{
// No ModelState errors are available to send, so just return an empty BadRequest.
return BadRequest();
}
return BadRequest(ModelState);
}
return null;
}
private class ExternalLoginData
{
public string LoginProvider { get; set; }
public string ProviderKey { get; set; }
public string UserName { get; set; }
public IList<Claim> GetClaims()
{
IList<Claim> claims = new List<Claim>();
claims.Add(new Claim(ClaimTypes.NameIdentifier, ProviderKey, null, LoginProvider));
if (UserName != null)
{
claims.Add(new Claim(ClaimTypes.Name, UserName, null, LoginProvider));
}
return claims;
}
public static ExternalLoginData FromIdentity(ClaimsIdentity identity)
{
if (identity == null)
{
return null;
}
Claim providerKeyClaim = identity.FindFirst(ClaimTypes.NameIdentifier);
if (providerKeyClaim == null || String.IsNullOrEmpty(providerKeyClaim.Issuer)
|| String.IsNullOrEmpty(providerKeyClaim.Value))
{
return null;
}
if (providerKeyClaim.Issuer == ClaimsIdentity.DefaultIssuer)
{
return null;
}
return new ExternalLoginData
{
LoginProvider = providerKeyClaim.Issuer,
ProviderKey = providerKeyClaim.Value,
UserName = identity.FindFirstValue(ClaimTypes.Name)
};
}
}
private static class RandomOAuthStateGenerator
{
private static RandomNumberGenerator _random = new RNGCryptoServiceProvider();
public static string Generate(int strengthInBits)
{
const int bitsPerByte = 8;
if (strengthInBits % bitsPerByte != 0)
{
throw new ArgumentException("strengthInBits must be evenly divisible by 8.", "strengthInBits");
}
int strengthInBytes = strengthInBits / bitsPerByte;
byte[] data = new byte[strengthInBytes];
_random.GetBytes(data);
return HttpServerUtility.UrlTokenEncode(data);
}
}
#endregion
}
Here's the ConfigureAuth method in StartUp.Auth.cs file
public void ConfigureAuth(IAppBuilder app)
{
// Configure the db context and user manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.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);
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
// 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),
// In production mode set AllowInsecureHttp = false
AllowInsecureHttp = true
};
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions);
// Uncomment the following lines to enable logging in with third party login providers
//app.UseMicrosoftAccountAuthentication(
// clientId: "",
// clientSecret: "");
//app.UseTwitterAuthentication(
// consumerKey: "",
// consumerSecret: "");
var facebookoptions = new FacebookAuthenticationOptions()
{
AppId = "",
AppSecret = "",
BackchannelHttpHandler = new FaceBookBackChannelHandler(),
UserInformationEndpoint = "https://graph.facebook.com/v2.4/me?fields=email,first_name,last_name,gender"
};
facebookoptions.Scope.Add("email");
app.UseFacebookAuthentication(facebookoptions);
//app.UseFacebookAuthentication(
// appId: "",
// appSecret: "");
app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions()
{
ClientId = "",
ClientSecret = ""
});
}
Also here's the jQuery methods that call the API endpoints
function getAccessToken() {
if (location.hash) {
if (location.hash.split('access_token=')) {
var accessToken = location.hash.split('access_token=')[1].split('&')[0];
if (accessToken) {
isUserRegistered(accessToken);
}
}
}
}
function isUserRegistered(accessToken) {
$.ajax({
url: 'http://localhostXXXX/api/Account/UserInfo',
method: 'GET',
cors:
{
headers: {
'content-type': 'application/JSON',
'Authorization': 'Bearer ' + accessToken
}
},
success: function (response) {
if (response.HasRegistered) {
localStorage.setItem('accessToken', accessToken);
localStorage.setItem('userName', response.Email);
window.location.href = "Data.aspx";
}
else {
signupExternalUser(accessToken);
}
}
});
}
function signupExternalUser(accessToken) {
$.ajax({
url: 'http://localhost:XXXXX/api/Account/RegisterExternal',
method: 'POST',
cors:
{
headers: {
'content-type': 'application/json',
'Authorization': 'Bearer ' + accessToken
}
},
success: function () {
window.location.href = "";
}
});
}
Here is the code which i used for getting my informations. its work fine:
// check if authorized
if (Request["code"] == null)
{
Response.Redirect(string.Format(
"https://graph.facebook.com/oauth/authorize?client_id={0}&redirect_uri={1}&scope={2}",
app_id, Request.Url.AbsoluteUri, scope));
}
else
{
FacebookAccessToken token = new FacebookAccessToken();
FacebookUser user = new FacebookUser();
//Requesting for access token
string url = string.Format("https://graph.facebook.com/oauth/access_token?client_id={0}&redirect_uri={1}&scope={2}&code={3}&client_secret={4}",
app_id, Request.Url.AbsoluteUri, scope, Request["code"].ToString(), app_secret);
HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
{
StreamReader reader = new StreamReader(response.GetResponseStream());
string vals = reader.ReadToEnd();
// Deserialize json object
token = JsonConvert.DeserializeObject<FacebookAccessToken>(data);
}
//Getting user info
url = string.Format("https://graph.facebook.com/v2.8/me?access_token={0}", token.AccessToken);
request = WebRequest.Create(url) as HttpWebRequest;
using (var client = request.GetResponse() as HttpWebResponse)
{
StreamReader reader = new StreamReader(client.GetResponseStream());
string data = reader.ReadToEnd();
// data:"{\"name\":\"Er Vatsal D Patel\",\"id\":\"13168723650*****\"}";
user = JsonConvert.DeserializeObject<FacebookUser>(data);
}
return "done";
}

OAuth 2 refresh token invalid_grant

Following Taiseer Joudeh excellent article
Enable OAuth Refresh Tokens in AngularJS App using ASP .NET Web API 2, and Owin (http://bitoftech.net/2014/07/16/enable-oauth-refresh-tokens-angularjs-app-using-asp-net-web-api-2-owin/) currently I am creating a Token based authentication with refresh token option.
My Startup class code is as follows:
public class Startup
{
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
ConfigureOAuth(app);
WebApiConfig.Register(config);
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
app.UseWebApi(config);
}
public void ConfigureOAuth(IAppBuilder app)
{
OAuthAuthorizationServerOptions oAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(30),
Provider = new SimpleAuthorizationServerProvider(),
RefreshTokenProvider = new SimpleRefreshTokenProvider()
};
// Token Generation
app.UseOAuthAuthorizationServer(oAuthServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
}
}
My SimpleAuthorizationServerProvider class code is as follows:
public class SimpleAuthorizationServerProvider : OAuthAuthorizationServerProvider
{
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
string clientId = string.Empty;
string clientSecret = string.Empty;
Client client = null;
if (!context.TryGetBasicCredentials(out clientId, out clientSecret))
{
context.TryGetFormCredentials(out clientId, out clientSecret);
}
if (context.ClientId == null)
{
//Remove the comments from the below line context.SetError, and invalidate context
//if you want to force sending clientId/secrects once obtain access tokens.
context.Validated();
//context.SetError("invalid_clientId", "ClientId should be sent.");
return Task.FromResult<object>(null);
}
using (AuthRepository _repo = new AuthRepository())
{
client = _repo.FindClient(context.ClientId);
}
if (client == null)
{
context.SetError("invalid_clientId", string.Format("Client '{0}' is not registered in the system.", context.ClientId));
return Task.FromResult<object>(null);
}
if (client.ApplicationType == Models.ApplicationTypes.NativeConfidential)
{
if (string.IsNullOrWhiteSpace(clientSecret))
{
context.SetError("invalid_clientId", "Client secret should be sent.");
return Task.FromResult<object>(null);
}
else
{
if (client.Secret != Helper.GetHash(clientSecret))
{
context.SetError("invalid_clientId", "Client secret is invalid.");
return Task.FromResult<object>(null);
}
}
}
if (!client.Active)
{
context.SetError("invalid_clientId", "Client is inactive.");
return Task.FromResult<object>(null);
}
context.OwinContext.Set<string>("as:clientAllowedOrigin", client.AllowedOrigin);
context.OwinContext.Set<string>("as:clientRefreshTokenLifeTime", client.RefreshTokenLifeTime.ToString());
context.Validated();
return Task.FromResult<object>(null);
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var allowedOrigin = context.OwinContext.Get<string>("as:clientAllowedOrigin");
if (allowedOrigin == null) allowedOrigin = "*";
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin });
//var id = "";
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;
}
//Here set User.Identity.Id = RavenUserId, So rest of the user will be able to get it
//id = (user == null ? "0" : user.RavenUserId.ToString());
}
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
//So when we will call User.Identity.Id we will be able to get Raven User Id
// identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, id));
identity.AddClaim(new Claim("sub", context.UserName));
identity.AddClaim(new Claim("role", "user"));
var props = new AuthenticationProperties(new Dictionary<string, string>
{
{
"as:client_id", (context.ClientId == null) ? string.Empty : context.ClientId
},
{
"userName", context.UserName
}
});
var ticket = new AuthenticationTicket(identity, props);
context.Validated(ticket);
}
public override Task GrantRefreshToken(OAuthGrantRefreshTokenContext context)
{
var originalClient = context.Ticket.Properties.Dictionary["as:client_id"];
var currentClient = context.ClientId;
if (originalClient != currentClient)
{
context.SetError("invalid_clientId", "Refresh token is issued to a different clientId.");
return Task.FromResult<object>(null);
}
// Change auth ticket for refresh token requests
var newIdentity = new ClaimsIdentity(context.Ticket.Identity);
var newClaim = newIdentity.Claims.Where(c => c.Type == "newClaim").FirstOrDefault();
if (newClaim != null)
{
newIdentity.RemoveClaim(newClaim);
}
newIdentity.AddClaim(new Claim("newClaim", "newValue"));
var newTicket = new AuthenticationTicket(newIdentity, context.Ticket.Properties);
context.Validated(newTicket);
return Task.FromResult<object>(null);
}
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);
}
}
My SimpleRefreshTokenProvider class code is as follows:
public class SimpleRefreshTokenProvider : IAuthenticationTokenProvider
{
public async Task CreateAsync(AuthenticationTokenCreateContext context)
{
var clientid = context.Ticket.Properties.Dictionary["as:client_id"];
if (string.IsNullOrEmpty(clientid))
{
return;
}
var refreshTokenId = Guid.NewGuid().ToString("n");
using (var _repo = new AuthRepository())
{
var refreshTokenLifeTime = context.OwinContext.Get<string>("as:clientRefreshTokenLifeTime");
var token = new RefreshToken()
{
Id = Helper.GetHash(refreshTokenId),
ClientId = clientid,
Subject = context.Ticket.Identity.Name,
IssuedUtc = DateTime.UtcNow,
ExpiresUtc = DateTime.UtcNow.AddMinutes(Convert.ToDouble(refreshTokenLifeTime))
};
context.Ticket.Properties.IssuedUtc = token.IssuedUtc;
context.Ticket.Properties.ExpiresUtc = token.ExpiresUtc;
token.ProtectedTicket = context.SerializeTicket();
var result = await _repo.AddRefreshToken(token);
if (result)
{
context.SetToken(refreshTokenId);
}
}
}
public async Task ReceiveAsync(AuthenticationTokenReceiveContext context)
{
var allowedOrigin = context.OwinContext.Get<string>("as:clientAllowedOrigin");
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin });
string hashedTokenId = Helper.GetHash(context.Token);
using (var _repo = new AuthRepository())
{
var refreshToken = await _repo.FindRefreshToken(hashedTokenId);
if (refreshToken != null)
{
//Get protectedTicket from refreshToken class
context.DeserializeTicket(refreshToken.ProtectedTicket);
var result = await _repo.RemoveRefreshToken(hashedTokenId);
}
}
}
public void Create(AuthenticationTokenCreateContext context)
{
throw new NotImplementedException();
}
public void Receive(AuthenticationTokenReceiveContext context)
{
throw new NotImplementedException();
}
}
The AuthRepository class code is as follows:
public class AuthRepository : IDisposable
{
private AuthContext _ctx;
private UserManager<IdentityUser> _userManager;
public AuthRepository()
{
_ctx = new AuthContext();
_userManager = new UserManager<IdentityUser>(new UserStore<IdentityUser>(_ctx));
}
public async Task<IdentityResult> RegisterUser(UserModel userModel)
{
IdentityUser user = new IdentityUser
{
UserName = userModel.UserName
};
var result = await _userManager.CreateAsync(user, userModel.Password);
return result;
}
public async Task<IdentityUser> FindUser(string userName, string password)
{
IdentityUser user = await _userManager.FindAsync(userName, password);
return user;
}
public Client FindClient(string clientId)
{
var client = _ctx.Clients.Find(clientId);
//var clients = _ctx.Clients;
//var client = _ctx.Clients.FirstOrDefault(x => x.Id==clientId);
return client;
}
public async Task<bool> AddRefreshToken(RefreshToken token)
{
var existingToken = _ctx.RefreshTokens.Where(r => r.Subject == token.Subject && r.ClientId == token.ClientId).SingleOrDefault();
if (existingToken != null)
{
var result = await RemoveRefreshToken(existingToken);
}
_ctx.RefreshTokens.Add(token);
return await _ctx.SaveChangesAsync() > 0;
}
public async Task<bool> RemoveRefreshToken(string refreshTokenId)
{
var refreshToken = await _ctx.RefreshTokens.FindAsync(refreshTokenId);
if (refreshToken != null)
{
_ctx.RefreshTokens.Remove(refreshToken);
return await _ctx.SaveChangesAsync() > 0;
}
return false;
}
public async Task<bool> RemoveRefreshToken(RefreshToken refreshToken)
{
_ctx.RefreshTokens.Remove(refreshToken);
return await _ctx.SaveChangesAsync() > 0;
}
public async Task<RefreshToken> FindRefreshToken(string refreshTokenId)
{
var refreshToken = await _ctx.RefreshTokens.FindAsync(refreshTokenId);
return refreshToken;
}
public List<RefreshToken> GetAllRefreshTokens()
{
return _ctx.RefreshTokens.ToList();
}
public void Dispose()
{
_ctx.Dispose();
_userManager.Dispose();
}
}
And the ajax code is:
$("#refresh").click(function () {
var token = sessionStorage.getItem(tokenKey);
var refresh = sessionStorage.getItem('isRefreshToken');
var refreshToken = sessionStorage.getItem('refreshToken');
if (refresh) {
var refreshdata = "grant_type=refresh_token&refresh_token=" + refreshToken + "&client_id=TokenBasedAuthentication";
console.log(refreshdata);
sessionStorage.setItem(tokenKey, '');
sessionStorage.setItem(isRefreshToken, '');
sessionStorage.setItem(refreshToken, '');
$.ajax({
url: '/token',
type: 'POST',
data: refreshdata,
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
success: function (data) {
sessionStorage.setItem(tokenKey, data.access_token);
sessionStorage.setItem(isRefreshToken, true);
sessionStorage.setItem(refreshToken, data.refresh_token);
},
error: function (xhr) {
alert(xhr.status + ': ' + xhr.statusText);
}
});
}
});
Finally when I click on Refresh it returns me following error
error: "invalid_grant"
Last two days I tried to figure out but failed.
I had a problem where I was always receiving a invalid_grant error even though I knew the refresh_token was valid. Granted there are a lot of reasons why there might be a invalid_grant error, but after debugging through the code I discovered that my issue was in the CreateAsync method. The refreshTokenLifetime variable was null. Thus, when the RefreshToken is created, the ExpiresUtc value is already expired, causing the invalid_grant error. To resolve this I verified that I had a valid value for the refreshTokenLifetime variable.
var refreshTokenLifetime = context.OwinContext.Get<string>("as:RefreshTokenLifetime") ?? "60";
Try this.
Remove this line of code newIdentity.AddClaim(new Claim("newClaim", "newValue")); from your GrantRefreshToken function of SimpleAuthorizationServerProvider class. As this line is of no use.
It is duplicating the claim when you request for new refresh token. So it is opposing you.
In ReceiveAsync method in SimpleRefreshTokenProvider class:
var refreshToken = await _repo.FindRefreshToken(hashedTokenId);
inspect refreshToken object to make sure all of its attributes have valid values.
In my case ProtectedTicket property has invalid value (Date value instead of string) and it causes this error.

Resources