I am trying to write my own authentication, so I inherited CredentialsAuthProvider and have overridden the Authenticate method. Auth is working fine, also when i call another service i can see all data that i saved in the session.
The Problem is: When i try add the Authenticate attribute and call it from a client, it goes and throws an Unauthorized exception, even if i want to use Requered Role.
Auth service is:
public class CustomCredentialsAuthProvider : CredentialsAuthProvider
{
public override bool TryAuthenticate(IServiceBase authService, string userName, string password)
{
return true;
}
public override object Authenticate(IServiceBase authService, IAuthSession session, Auth request)
{
session.FirstName = "Name";
//...
session.Authenticate = true;
session.UserName = request.UserName;
session.Roles = new List<string>;
session.Roles.Add("admin")
//....
authService.SaveSession(session, SessionExpiry);
// Return custom object
return new UserAuthResponse { SessionId = session.Id ......};
}
AppHost is:
public override void Configure(Container container)
{
Plugins.Add(new AuthFeature(() => new AuthUserSession(), new IAuthProvider[] {
new CustomCredentialsAuthProvider()
}));
Plugins.Add(new RegistrationFeature());
container.Register<ICacheClient>(new MemoryCacheClient());
var userRep = new InMemoryAuthRepository();
container.Register<IUserAuthRepository>(userRep);
}
and test service:
[Authenticate]
public class TestService : Service {
public object Any(UserRequest request) {
return new UserResponse{Name = request.Name};
}
}
It is not real code, so sorry for syntax mistake!))))
But the idea is the same! Help me please what is wrong, why I got Unauthorized exception when i call Test service??????????
When I had this issue, I had to create a custom authenticate attribute [CustomAuthenticate] with guidance from this gist -> https://gist.github.com/joeriks/4518393
In the AuthenticateIfBasicAuth method, I set provider to use MyAuthProvider.Name instead of BasicAuthProvider.Name
Then,
[CustomAuthenticate]
public class TestService : Service {
public object Any(UserRequest request) {
return new UserResponse{Name = request.Name};
}
}
Also see: http://joeriks.com/2013/01/12/cors-basicauth-on-servicestack-with-custom-authentication/
Related
I'm testing some custom authorization without the default Entity Framework stuff.
I have created an "ASP.NET Core Web App (Model-View-Controller)" project using "Authentication type" = "Individual Accounts".
In Program.cs I have:
builder.Services.AddTransient<IUserStore<CustomIdentityUser>, CustomUserStore>();
builder.Services
.AddDefaultIdentity<CustomIdentityUser>()
.AddUserStore<CustomUserStore>();
For the moment CustomIdentityUser is just an empty class.
CustomUserStore looks like this:
public class CustomUserStore : IUserStore<CustomIdentityUser>
{
public void Dispose()
{
GC.SuppressFinalize(this);
}
public Task<CustomIdentityUser> FindByNameAsync(string normalizedUserName, CancellationToken cancellationToken)
{
// Use dummy user for now
//return new Task<CustomIdentityUser>(() => new CustomIdentityUser());
return new Task<CustomIdentityUser>(() => { throw new Exception("THIS DOES NOT HAPPEN!"); });
}
...
(All other methods from IUserStore currently throws NotImplementedException.)
If I start the application, go the the login page, enter some credentials and click login I can see that FindByNameAsync in CustomUserStore is called. Good, it seems the application actually uses my custom user store to look for the user whose name I just entered.
But that's where my luck ends. The user interface seems to be waiting for the login to complete. The Task returned from FindByNameAsync doesn't seem to be started at all...why? I think that the caller should get CustomIdentityUser instance from it (and then probably call GetPasswordHashAsync in CustomUserStore).
why do you return new task in FindByNameAsync , also CustomUserStore
should be for extending identity properties. in my opinion, creating a service for identity functionality and using dependency injection
create an interface with the name IIdentity.cs
public Task<IdentityResult> UpdateUserAsync(string id , UpdateUserVm updatedUser);
then the implementation class IdentityService
public class IdentityService : IIdentity
{
...
public async Task<IdentityResult> UpdateUserAsync(string id, UpdateUserVm updatedUser)
{
var currentUser = await _userManager.FindByIdAsync(id);
currentUser.PhoneNumber = updatedUser.PhoneNumber;
return currentUser != null ? await _userManager.UpdateAsync(currentUser) : IdentityResult.Failed();
}
}
then register the service
service.AddScoped<IIdentity, IdentityService>();
in controller
public class AccountController : Controller
{
private readonly IIdentity _identityService;
public AccountController(IIdentity identityService) =>
(_identityService) = (identityService);
[HttpPost]
public async Task<IActionResult> UpdateProfile(params)
{
...
await _identityService.UpdateUserAsync(params)
}
}
I am trying to authenticate the user in my WASM Blazor app using google's OIDC.
I have managed to retrieve the token by following this article: https://learn.microsoft.com/en-gb/aspnet/core/blazor/security/webassembly/standalone-with-authentication-library?view=aspnetcore-3.1&tabs=visual-studio
I am trying to retrieve the AccessToken to pass it to the SignalR hub using the injected instance of IAccessTokenProvider when building an instance of HubConnection:
public RemoteCombatListener(ITokenCache tokenCache)
{
_connection = new HubConnectionBuilder()
.WithUrl("https://localhost:44364/combat", opts => {
opts.AccessTokenProvider = tokenCache.GetToken;
})
.Build();
}
Here is the implementation of my TokenCache:
public class TokenCache : ITokenCache
{
private readonly IAccessTokenProvider _tokenProvider;
private readonly NavigationManager _navManager;
public string CachedToken { get; private set; }
public TokenCache(IAccessTokenProvider tokenProvider, NavigationManager navManager)
{
_tokenProvider = tokenProvider;
_navManager = navManager;
}
public async Task<string> GetToken()
{
if (string.IsNullOrEmpty(CachedToken))
{
var requestedToken = await _tokenProvider.RequestAccessToken();
if (requestedToken.TryGetToken(out var accessToken))
{
CachedToken = accessToken.Value;
}
else
{
throw new AccessTokenNotAvailableException(_navManager, requestedToken, Enumerable.Empty<string>());
}
}
return CachedToken;
}
}
The problem I am facing right now is that when calling the _tokenProvider.RequestAccessToken() method, I get the following exception:
An exception occurred executing JS interop: The JSON value could not be converted to System.DateTimeOffset. Path: $.token.expires | LineNumber: 0 | BytePositionInLine: 80.. See InnerException for more details.
I am unable to figure out what is wrong with my setup as debugging stopped working for me randomly and the only option I have is Console.Log debugging.
It turns out that default configuration for the Oidc doesn't request access_token, only id_token. Had to add the following:
builder.Services.AddOidcAuthentication(options => {
// Rest of configs ...
options.ProviderOptions.ResponseType = "id_token token";
});
In a WebAPI .net core project I have created a Middleware class that validates an api key. From validating it, it retrieves the permissions that the key has (user or admin) within the invoke method.
I pass it through a switch to set the principle like so
GenericIdentity identity = new GenericIdentity("API");
GenericPrincipal principle = null;
//we have a valid api key, so set the role permissions of the key
switch (keyValidatorRes.Role)
{
case Roles.User:
principle = new GenericPrincipal(identity, new[] { "User" });
context.User = principle;
break;
case Roles.Admin:
principle = new GenericPrincipal(identity, new[] { "Admin" });
context.User = principle;
break;
default:
principle = new GenericPrincipal(identity, new[] { "Other" });
context.User = principle;
break;
}
On controllers methods I have
[Authorize(Roles = "Admin")]
to validate the roles of an authenticated api key
If the user has the admin principle it goes through as expected. However, if it has a user or other principle then I get an error about
not having a DefaultForbidScheme
I googled around and added Authentication to my startup.cs with a customer scheme
services.AddAuthentication(options=> {
options.DefaultForbidScheme = "forbidScheme";
options.AddScheme<AuthSchemeHandle>("forbidScheme", "Handle Forbidden");
});
and created the AuthSchemeHandle
public class AuthSchemeHandle : IAuthenticationHandler
{
private HttpContext _context;
public Task<AuthenticateResult> AuthenticateAsync()
{
return Task.FromResult(AuthenticateResult.NoResult());
}
public Task ChallengeAsync(AuthenticationProperties properties)
{
throw new NotImplementedException();
}
public Task ForbidAsync(AuthenticationProperties properties)
{
return Task.FromResult(AuthenticateResult.Fail("Failed Auth"));
}
public Task InitializeAsync(AuthenticationScheme scheme, HttpContext context)
{
_context = context;
return Task.CompletedTask;
}
}
Now if the principle does not have Admin it fails without the error but the response that is returned on the API is 200 with no content. I was expecting a 4xx response with the message "Failed Auth"
I am just trying to work out why it is not as expected as although it seems "fixed" I do not understand how it has fixed it.
Is there a better way that I should be doing this?
why it is not as expected as although it seems "fixed" I do not understand how it has fixed it.
There's no dark magic when the authentication handler calls IAuthenticationHandler.ForbidAsync() method. We have to do relevant things ourself. In short, setting the StatusCode=403 as your need.
public async Task ForbidAsync(AuthenticationProperties properties)
{
properties = properties ?? new AuthenticationProperties();
_context.Response.StatusCode = 403;
// ...
return Task.CompletedTask;
}
As a side note, you don't need return a Task.FromResult() as it doesn't care about the result.
Is there a better way that I should be doing this?
The ASP.NET Core Team provides us an abstract class AuthenticationHandler to handle authentication. This abstract class has a built-in implementation for ForbidAsync(AuthenticationProperties properties) (and also for other public methods). So it's much easy to extends this abstract class as below:
public class MyAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
public MyAuthenticationHandler(IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock)
: base(options, logger, encoder, clock)
{
}
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
return AuthenticateResult.NoResult();
}
}
Finally, add a configuration for authentication service:
services
.AddAuthentication(options=>{
options.DefaultAuthenticateScheme = "forbidScheme";
options.DefaultForbidScheme = "forbidScheme";
options.AddScheme<MyAuthenticationHandler>("forbidScheme", "Handle Forbidden");
});
It should work as expected.
I Had create a MVC4 Web API. But People without authorization also can use it.
Example: people type in address bar "/api/product/1" also can get the result.
So, How to implement Security and allow authorize person to use the WEB API only ?
How to give authorize to the person that allow login to web api ?
More info about Authentication and Authorization
Simply adding the annotation to your controller will do:
// Require authorization for all actions on the controller.
[Authorize]
public class ValuesController : ApiController
{
public HttpResponseMessage Get(int id) { ... }
public HttpResponseMessage Post() { ... }
}
// Restrict by user:
[Authorize(Users="Alice,Bob")]
public class ValuesController : ApiController
{
}
// Restrict by role:
[Authorize(Roles="Administrators")]
public class ValuesController : ApiController
{
}
you can use MVC4 AspNet.identiy.Usermanager and Microsoft.Owin.Security to authenticate user..
private IAuthenticationManager AuthenticationManager
{
get
{
return HttpContext.Current.GetOwinContext().Authentication;
}
}
public HttpResponseMessage Login(string username, string password)
{
UserManager<TenantUser> userManager=new new UserManager<TenantUser>(new UserStore<TenantUser>(YOUR DBCONTEXT));
var user = UserManager.Find(username, password);
if (user != null)
{
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicatioCookie);
ClaimsIdentity identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = true }, identity);
}
else
new HttpResponseMessage(HttpStatusCode.Forbidden) { Content = new ObjectContent<object>(new { Error = "You are not authorized to perform this action" }, Configuration.Formatters.JsonFormatter) };
}
it is working for me....
Using ServiceStack, I have to selectively enable authentication on services, request DTOs and actions by applying the [Authenticate] attribute on the respective classes/methods.
Is it possible to do the inverse? I.e. globally enable authentication for all services/requests and then selectively disable authentication for some requests (e.g. using something like a [NoAuthentication] attribute on the relevant parts)?
Create a Request Filter Attribute that sets a flag in the request context saying to skip authentication:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
public class NoAuthenticateAttribute : RequestFilterAttribute {
public NoAuthenticateAttribute() : this(ApplyTo.All) {}
public NoAuthenticateAttribute(ApplyTo applyTo) : base(applyTo) {
// execute this before any AuthenticateAttribute executes.
// https://github.com/ServiceStack/ServiceStack/wiki/Order-of-Operations
Priority = this.Priority = ((int) RequestFilterPriority.Authenticate) - 1;
}
public override void Execute(IHttpRequest req, IHttpResponse res, object requestDto)
{
req.Items["SkipAuthentication"] = true;
}
}
And create a custom subclass of AuthenticateAttribute that checks for that flag in the request:
public class MyAuthenticateAttribute : AuthenticateAttribute {
public override void Execute(IHttpRequest req, IHttpResponse res, object requestDto)
{
if (!ShouldSkipAuthenticationFor(req))
base.Execute(req, res, requestDto);
}
private bool ShouldSkipAuthenticationFor(IHttpRequest req)
{
return req.Items.ContainsKey("SkipAuthentication");
}
}
Usage:
[MyAuthenticate]
public class MyService : Service
{
public object Get(DtoThatNeedsAuthentication obj)
{
// this will be authenticated
}
[NoAuthenticate]
public object Get(DtoThatShouldNotAuthenticate obj)
{
// this will not be authenticated
}
}