I am running dotnet Web API with basic OAuth token authentication, but i have to configure this API to authenticate from Keycloak server.
I have to configure my asp.net mvc web api (.net framework 4.7) or dotnet core 3 api with keycloak. i have read the keycloak docs but there is no certain examples or adapters so that i can configure my REST API.
This is my startup.cs
public partial class Startup
{
public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }
public static string PublicClientId { get; private set; }
public void ConfigureAuth(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(PublicClientId),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromHours(6),
AllowInsecureHttp = false
};
app.UseOAuthBearerTokens(OAuthOptions);
}
}
This is the OAuthProvider Class
public class ApplicationOAuthProvider : OAuthAuthorizationServerProvider
{
private readonly string _publicClientId;
public ApplicationOAuthProvider(string publicClientId)
{
if (publicClientId == null)
{
throw new ArgumentNullException(nameof(publicClientId));
}
_publicClientId = publicClientId;
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
string usernameVal = context.UserName;
string passwordVal = context.Password;
long convertusernametoid = long.Parse(usernameVal);
var user = "userfromdb";
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
var claims = new List<Claim>();
var username = "username";
claims.Add(new Claim(ClaimTypes.Name, username));
ClaimsIdentity oAuthClaimIdentity = new ClaimsIdentity(claims, OAuthDefaults.AuthenticationType);
ClaimsIdentity cookiesClaimIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationType);
AuthenticationProperties properties = CreateProperties(username);
AuthenticationTicket ticket = new AuthenticationTicket(oAuthClaimIdentity, properties);
context.Validated(ticket);
context.Request.Context.Authentication.SignIn(cookiesClaimIdentity);
}
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)
{
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> { };
return new AuthenticationProperties(data);
}
}
Please help me with keycloak configuration to my Web Api.
Is it possible to update this code to get token from keycloak, if yes please guide me with configuration or any helpful resource.
Thank You.
Related
I am trying to undesratnd owin and OAuthAuthorizationServer.
I know that Outh2 has 4 parts:
1- Resource Owner
2- Resource Server:
3- Client Applications:
4- Authorization Server:
I have implemnted owin and Authorization Server in a simple application.
The application is working fine.
I just want to learn more regading the Outh stuffs.
so I have in my srartp class :
public class Startup
{
public void ConfigureAuth(IAppBuilder app)
{
app.UseCors(CorsOptions.AllowAll);//this is very important line cross orgin source(CORS)it is used to enable cross-site HTTP requests //For security reasons, browsers restrict cross-origin HTTP requests
var OAuthOptions = new OAuthAuthorizationServerOptions
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(60),//token expiration time
Provider = new OauthProvider()
};
app.UseOAuthBearerTokens(OAuthOptions);
app.UseOAuthAuthorizationServer(OAuthOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
HttpConfiguration config = new HttpConfiguration();
WebApiConfig.Register(config);//register the request
}
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
GlobalConfiguration.Configure(WebApiConfig.Register);
}
}
Then I created my OauthProvider
here is my class
public class OauthProvider : OAuthAuthorizationServerProvider
{
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
//First request will come here, this method will validate the request wheather it has crendtials(UserName and Password) if the request not contain username and
//password the request will reject from here not proceded any further
context.Validated();
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
//If the request has valid and it contain username and password than this method will check correct crenstials and than generate a valid token
var identity = new ClaimsIdentity(context.Options.AuthenticationType); //it will check the authenticate type
using (var db = new DataContext())
{
if (db != null)
{
var user = db.Users.Where(o => o.UserName == context.UserName && o.Password == context.Password).FirstOrDefault();
if (user != null)
{
//Store information againest the request
identity.AddClaim(new Claim("UserName", context.UserName));
identity.AddClaim(new Claim("LoggedOn", DateTime.Now.ToString()));
context.Validated(identity);
}
else
{
context.SetError("Wrong Crendtials", "Provided username and password is incorrect");
context.Rejected();
}
}
else
{
context.SetError("Wrong Crendtials", "Provided username and password is incorrect");
context.Rejected();
}
return;
}
}
}
So if I want to undersatnd the OAuth parts.
How can I define what I have done to each part ?
Notice That this is a web api project ?
Any useful informatuin is helpful.
Thanks
When token generate with flowing my own condition at that time I want to fetch some data of login user.
I'm already done access token generate
Here is my Startup class:
public class Startup
{
public void Configuration(IAppBuilder app)
{
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=316888
app.UseCors(CorsOptions.AllowAll);
var myProvider = new MyAuthorizationServerProvider();
OAuthAuthorizationServerOptions options = new OAuthAuthorizationServerOptions
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = myProvider
};
app.UseOAuthAuthorizationServer(options);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
HttpConfiguration config = new HttpConfiguration();
WebApiConfig.Register(config);
}
}
MyAuthorizationServerProvider class
public class MyAuthorizationServerProvider : OAuthAuthorizationServerProvider
{
private readonly ReviewDbContext db;
public MyAuthorizationServerProvider()
{
db = new ReviewDbContext();
}
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var user = db.Reviewers.Where(x => x.Name == context.UserName && x.Password == context.Password).FirstOrDefault();
var admin = db.Admins.Where(x => x.Name == context.UserName && x.Password == context.Password).FirstOrDefault();
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
if (admin != null && user == null)
{
identity.AddClaim(new Claim(ClaimTypes.Role, "admin"));
identity.AddClaim(new Claim("UserName", admin.Name));
identity.AddClaim(new Claim(ClaimTypes.Name, "Admin Ahasanul Banna"));
context.Validated(identity);
}
else if (user != null)
{
identity.AddClaim(new Claim(ClaimTypes.Role, "user"));
identity.AddClaim(new Claim("UserName", user.Name));
identity.AddClaim(new Claim(ClaimTypes.Name, "User Ahasanul Banna"));
context.Validated(identity);
}
else
{
context.SetError("Invalid_grant", "Provided username & password is incorrect");
return;
}
}
}
AuthorizeAttribute class
public class AuthorizeAttribute :System.Web.Http.AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(HttpActionContext actionContext)
{
if (!HttpContext.Current.User.Identity.IsAuthenticated)
{
base.HandleUnauthorizedRequest(actionContext);
}
else
{
actionContext.Response = new HttpResponseMessage(HttpStatusCode.Forbidden);
}
}
}
Postman
My expected output like as:
Where I set user data which I want with user generate token.
How to achieve this?
You are adding claims to your token so in order to access them you need to decode the token. However, if you want your extra data to be outside the token (like the image you have painted), you can add them as different properties to the login response object:
var props = new AuthenticationProperties(new Dictionary<string, string>
{
{
"UserName", "AA"
},
{
"UserId" , "1"
}
});
var ticket = new AuthenticationTicket(identity, props);
context.Validated(ticket);
Also, you need to add the following method to MyAuthorizationServerProvider :
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);
}
I am new to WEB API ,here I need to implement a token based authentication for a login page .
As of now I have completed the token generate functions by providing static values for learning purpose .
Now What I want to do is I want to validate each request with the table which I have created in my SQL SERVER (LOGIN_TABLE) and generate Token instead of validate with Identity tables in web API.
Columns of LOGIN_TABLE
FirstName,LastName,UserName,Email,Password,CreationDate
I don't know is it possible or not ,if possible please help me to do that .
I have created my project as Web API template without MVC and choosed No Authentication then I have added all the necessary packages and classes.
Here I have added the code I have used to generate token based on the static values.
My_Controller.cs
[Authorize]
[HttpGet]
[Route("api/data/authenticate")]
public IHttpActionResult GetForAuthenticate()
{
var identity = (ClaimsIdentity)User.Identity;
return Ok("Hello" + identity.Name);
}
MyAutorizationServerProvider.cs
public class MyAuthorizationServerProvider :OAuthAuthorizationServerProvider
{
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
// Resource owner password credentials does not provide a client ID.
context.Validated();
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
if (context.UserName == "user" && context.Password == "123")
{
identity.AddClaim(new Claim(ClaimTypes.Role, "USER"));
identity.AddClaim(new Claim("username", "user"));
identity.AddClaim(new Claim(ClaimTypes.Name, "Sakthi"));
context.Validated(identity);
}else
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
}
}
Startup.cs
public class Startup
{
public void Configuration(IAppBuilder app)
{
// For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=316888
//This enable cors orgin requests
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
var myProvider = new MyAuthorizationServerProvider();
OAuthAuthorizationServerOptions options = new OAuthAuthorizationServerOptions
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(30),
Provider = myProvider
};
app.UseOAuthAuthorizationServer(options);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
HttpConfiguration config = new HttpConfiguration();
WebApiConfig.Register(config);
}
}
AuthorizeAttribute.cs
public class AuthorizeAttribute :System.Web.Http.AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(System.Web.Http.Controllers.HttpActionContext actioncontext)
{
if (!HttpContext.Current.User.Identity.IsAuthenticated)
{
base.HandleUnauthorizedRequest(actioncontext);
}else
{
actioncontext.Response = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.Forbidden);
}
}
}
I am new to web API if I did any mistakes here please guide me to correct it .
Thanks .
I created pone webapi and implemented authentication. I have the token method to get the user token. All working fine.
Scenario:
I tested the token method with the postman. Here I noted that I can use any type of HTTP method to request for the token. I think /token method should support POST method only. But when I use DELETE method also I got token. Same as, I can also use PUT, PATH etc.
Is this expected? I assume that it should return Method Not Supported other than POST requests.
You can write your custom OAuthAuthorizationServerOptions.Provider. And use the context to accept Http Post request only
OAuthAuthorizationServerOptions is asp.net identity core class. which you can find under this namespace Microsoft.Owin.Security.OAuth.
Sample Code:
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
}
public void ConfigureOAuth(IAppBuilder app)
{
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new SimpleAuthorizationServerProvider()
};
// Token Generation
app.UseOAuthAuthorizationServer(OAuthServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
}
}
public class SimpleAuthorizationServerProvider : OAuthAuthorizationServerProvider
{
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
if (context.Request.Method != "POST")
{
context.SetError("invalid_request", "Invalid request");
return;
}
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;
}
}
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim("sub", context.UserName));
identity.AddClaim(new Claim("role", "user"));
context.Validated(identity);
}
}
I'm using in my project asp.net web api 2, token authorize and when send request to /Token,I receive response
{
access_token: "u3XOCYV91f2P6odbceNIY_BnkfSpN7gQwzknsRi_.......0iRPlHYNMEES9",
token_type: "bearer",
expires_in: 1209599,
}
There is no argument UserName =( and when i send the request to the server with token on Authorize ActionResult,everything works well, but User.Identity.Name is null, User.Identity.isAuthenticated = true. Help pls.
public class SimpleAuthorizationServerProvider : OAuthAuthorizationServerProvider
{
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
using (AuthorizeRepository _repo = new AuthorizeRepository())
{
User user = await _repo.FindUser(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
}
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim("sub", context.UserName));
identity.AddClaim(new Claim("role", "user"));
context.Validated(identity);
}
}
public void ConfigureOAuth(IAppBuilder app)
{
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new SimpleAuthorizationServerProvider(),
};
// Token Generation
app.UseOAuthAuthorizationServer(OAuthServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
}
For User.Identity.Name to be automatically populated, you must add a ClaimTypes.Name claim in your access token:
identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));