Need to pass additional value to UserNameSecurityToken in STS from client application - wif

I have incorporate security into my wcf service using wif. Below my high level design.
Wif sts application - Here i have used custom username security token handler for validate the usename & passsword
Wcf service - list of services
Web application -> where i consumed the wcf service.
STS custom username security token handler as follows:
public class CustomUserNameSecurityTokenHandler : UserNameSecurityTokenHandler
{
public override Microsoft.IdentityModel.Claims.ClaimsIdentityCollection ValidateToken(System.IdentityModel.Tokens.SecurityToken token)
{
UserNameSecurityToken userNameToken = token as UserNameSecurityToken;
CredentialStore.AuthenticateUser(userNameToken.username, userNameToken.Password);
// ...
}
}
Code to consume the wcf service from web application
ClientCredentials oldCredentials = client.Endpoint.Behaviors.Remove<ClientCredentials>();
CachedClientCredentials newCredentials = new CachedClientCredentials(_tokenCache, oldCredentials);
client.Endpoint.Behaviors.Add(newCredentials);
client.ClientCredentials.UserName.UserName = "Admin"
client.ClientCredentials.UserName.Password = "password";
client.Authenticate();
While consume the wcf service i am able to send the username and password to STS validateToken method for authenticate and my scenario is like i want to send one more value (current web site address) to validatetoken method from consume part. i have workaround to send the additional value part of username but that is not the good idea to do that.
So could you please help me to resolve my issue?

An STS service that I have implemented requires a ClientID in addition to the username and password. I've solved this problem by adding custom elements into the security token request when initialising the service client. The STS service then reads out these values whilst authorizing the token and also passes back the ClientID in the claims.
// init client..
_serviceClient.ClientCredentials.UserName.UserName = Username;
_serviceClient.ClientCredentials.UserName.Password = Password;
var doc = new XmlDocument();
XmlElement customElement = doc.CreateElement("ExtraAuthData", Name, "http://localhost/STS/identity");
customElement.InnerText = Value;
(_serviceClient.Endpoint.Binding as WS2007FederationHttpBinding).Security.Message.TokenRequestParameters.Add(customElement);
Not sure if this is a recommended approach or not, I couldn't find any other way of doing this.

Related

jhipster Reload OIDC token after modifying user last name with keycloak rest admin API (Oauth2)

I have Jhipster running with Oauth2 + Keycloak.
I have a use case where I need to update user last and first name from the Jhipster React UI, so I used the Keycloak admin client via a service account to update user attributes in Keycloak.
The problem is that the information needs to be re-fetched to the OIDC token to let the user see the changes immediately. (similar issue here: https://github.com/jhipster/generator-jhipster/issues/7398 )
Is there any suggestion how to setup Spring Security to be able to re-fetch/refresh my token with the latest information form Keycloak, or any explicit call to do it?
Thanks for the answears!
So from workflow point of view I was able to solve the problem by:
Changing the data via Keycloak admin client
Change the data in the Spring Security Context
I had a wrong assumption about spring security that it validates the token data against the actual token stored in the context on every call. It turned out the spring security has no problem by changing the data in the context, so on the next login I can get a valid token what is inline with the actual data.
This is the code I was able to change the context with:
public void updateUserRole(AbstractAuthenticationToken abstractAuthenticationToken)
{
SecurityUtils.getCurrentUserLogin().flatMap(userRepository::findOneByLogin)
.ifPresent(user -> {
Set<Authority> authorities = user.getAuthorities();
Authority authority = new Authority();
authority.setName(AuthoritiesConstants.USER);
authorities.remove(AuthoritiesConstants.INVITED);
authorities.add(authority);
user.setAuthorities(authorities);
this.clearUserCaches(user);
log.debug("Changed Information for User: {}", user);
});
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
List<GrantedAuthority> authorities = List.of(new SimpleGrantedAuthority(AuthoritiesConstants.USER));
Map<String, Object> claims = ((OidcIdToken)((DefaultOidcUser)((OAuth2AuthenticationToken)abstractAuthenticationToken).getPrincipal()).getIdToken()).getClaims();
String userNameKey = ((OAuth2AuthenticationToken)authentication).getAuthorizedClientRegistrationId();
String tokenValue = ((OidcIdToken) ((DefaultOidcUser) ((OAuth2AuthenticationToken) abstractAuthenticationToken).getPrincipal()).getIdToken()).getTokenValue();
Instant issuedAt = ((OidcIdToken) ((DefaultOidcUser) ((OAuth2AuthenticationToken) abstractAuthenticationToken).getPrincipal()).getIdToken()).getIssuedAt();
Instant expiresAt = ((OidcIdToken) ((DefaultOidcUser) ((OAuth2AuthenticationToken) abstractAuthenticationToken).getPrincipal()).getIdToken()).getExpiresAt();
OidcIdToken oidcIdToken = new OidcIdToken(tokenValue, issuedAt, expiresAt, claims);
DefaultOidcUser user = new DefaultOidcUser(authorities, oidcIdToken, "name");
OAuth2AuthenticationToken oAuth2AuthenticationToken = new OAuth2AuthenticationToken(user, authorities, userNameKey);
SecurityContextHolder.getContext().setAuthentication(oAuth2AuthenticationToken);
}

Passing a parameter to Identity Server 4 Login Page

I have a client application that is using IDSVR4 for authentication. I need to store the user's username(in a different process from the login through IDSVR) on the client in a session or whatevr other client-side data storage mech so the user doesn't have to enter the details everytime he logs in from that specific browser.
How can i pass the username from the client to identity server?
You can add the username to the request parameters. If you're using asp.net in the client, you may use the notification event RedirectToIdentityProvider then add your username to the ProtocolMessage. Something like this:
RedirectToIdentityProvider = context =>
{
context.ProtocolMessage.Parameters['username'] = "John";
return Task.CompletedTask;
}
Any value you add to the parameters will be accessible in IdentityServer through the IIdentityServerInteractionService method GetAuthorizationContextAsync
Like this, in your IdentityServer controller:
public async Task<IActionResult> Login(string returnUrl){
var context = await _interaction.GetAuthorizationContextAsync(returnUrl);
var username = context.Parameters['username'];
...
}
You can pass custom parameter to the authorize endpoint , for code sample you can refer to my reply here .
If you are not using the OpenID Connect OWIN Middleware , you can directly put the custom parameter into the authorize request :
http://localhost:xxxx/account/login?returnUrl=/connect/authorize/callback?client_id=mvc2&redirect_uri=http%3A%2F%2Flocalhost%3A49459%2Fsignin-oidc&response_type=code%20id_token&scope=openid%20profile%20api1%20offline_access&response_mode=form_post&nonce=xxxx&state=xxxx
On identity server side you can parse returnUrl and easily get the parameter .

IdentityServer3 - redirect to ADFS if client is on intranet

We have a portal (mvc rdp) that is used by both internal users (employees) and external users (customers). We would like IdentityServer3 to automatically detect if the authentication request is done from within the corporate network, and redirect to ADFS. The local login should be shown if the user-agent is calling from the internet.
In short, we don't want to have buttons for the external idp as we want the IdSrv to automatically redirect to ADFS if client is on the internal network to provide true single sign on for our domain bound users.
If the portal was only used by internal users, then we would just configure the client to only use a particular identity provider but this portal is also used by external customers and those users are not stored in our AD ;)
I've looked at overriding PreAuthenticateAsync and using Dns.Dns.GetHostName() but that is related to the machine that IdentityServer is running on and not the client machine.
In an mvc controller, we would just use Request.UserHostName but this is not available in IdentityServer3 UserService.
I think you can get the client's IP address from the OwinContext; something like this:
public class UserService : UserServiceBase
{
OwinContext ctx;
public UserService(OwinEnvironmentService owinEnv)
{
ctx = new OwinContext(owinEnv.Environment);
}
public override Task PreAuthenticateAsync(PreAuthenticationContext context)
{
// The IP Address of the remote client
var ipAddress = ctx.Environment["server.RemoteIpAddress"].ToString();
if (BelongsToOurNetwork(ipAddress))
context.SignInMessage.IdP = "OurADFS";
else
context.SignInMessage.IdP = "idsrv"; // local login
return Task.FromResult(0);
}
}

How do I authorize access to ServiceStack resources using OAuth2 access tokens via DotNetOpenAuth?

I've created an OAuth2 authorization server using DotNetOpenAuth, which is working fine - I'm using the resource owner password flow, and successfully exchanging user credentials for an access token.
I now want to use that access token to retrieve data from secure endpoints in a ServiceStack API, and I can't work out how to do so. I've examined the Facebook, Google, etc. providers included with ServiceStack but it's not clear whether I should be following the same pattern or not.
What I'm trying to achieve (I think!) is
OAuth client (my app) asks resource owner ('Catherine Smith') for credentials
Client submits request to authorization server, receives an access token
Client requests a secure resource from the resource server (GET /users/csmith/photos)
The access token is included in an HTTP header, e.g. Authorization: Bearer 1234abcd...
The resource server decrypts the access token to verify the identity of the resource owner
The resource server checks that the resource owner has access to the requested resource
The resource server returns the resource to the client
Steps 1 and 2 are working, but I can't work out how to integrate the DotNetOpenAuth resource server code with the ServiceStack authorization framework.
Is there an example somewhere of how I would achieve this? I've found a similar StackOverflow post at How to build secured api using ServiceStack as resource server with OAuth2.0? but it isn't a complete solution and doesn't seem to use the ServiceStack authorization provider model.
EDIT: A little more detail. There's two different web apps in play here. One is the authentication/authorisation server - this doesn't host any customer data (i.e. no data API), but exposes the /oauth/token method that will accept a username/password and return an OAuth2 access token and refresh token, and also provides token-refresh capability. This is built on ASP.NET MVC because it's almost identical to the AuthorizationServer sample included with DotNetOpenAuth. This might be replaced later, but for now it's ASP.NET MVC.
For the actual data API, I'm using ServiceStack because I find it much better than WebAPI or MVC for exposing ReSTful data services.
So in the following example:
the Client is a desktop application running on a user's local machine, the Auth server is ASP.NET MVC + DotNetOpenAuth, and the Resource server is ServiceStack
The particular snippet of DotNetOpenAuth code that's required is:
// scopes is the specific OAuth2 scope associated with the current API call.
var scopes = new string[] { "some_scope", "some_other_scope" }
var analyzer = new StandardAccessTokenAnalyzer(authServerPublicKey, resourceServerPrivateKey);
var resourceServer = new DotNetOpenAuth.OAuth2.ResourceServer(analyzer);
var wrappedRequest = System.Web.HttpRequestWrapper(HttpContext.Current.Request);
var principal = resourceServer.GetPrincipal(wrappedRequest, scopes);
if (principal != null) {
// We've verified that the OAuth2 access token grants this principal
// access to the requested scope.
}
So, assuming I'm on the right track, what I need to do is to run that code somewhere in the ServiceStack request pipeline, to verify that the Authorization header in the API request represents a valid principal who has granted access to the requested scope.
I'm starting to think the most logical place to implement this is in a custom attribute that I use to decorate my ServiceStack service implementations:
using ServiceStack.ServiceInterface;
using SpotAuth.Common.ServiceModel;
namespace SpotAuth.ResourceServer.Services {
[RequireScope("hello")]
public class HelloService : Service {
public object Any(Hello request) {
return new HelloResponse { Result = "Hello, " + request.Name };
}
}
}
This approach would also allow specifying the scope(s) required for each service method. However, that seems to run rather contrary to the 'pluggable' principle behind OAuth2, and to the extensibility hooks built in to ServiceStack's AuthProvider model.
In other words - I'm worried I'm banging in a nail with a shoe because I can't find a hammer...
OK, after a lot of stepping through the various libraries with a debugger, I think you do it like this: https://github.com/dylanbeattie/OAuthStack
There's two key integration points. First, a custom filter attribute that's used on the server to decorate the resource endpoints that should be secured with OAuth2 authorization:
/// <summary>Restrict this service to clients with a valid OAuth2 access
/// token granting access to the specified scopes.</summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true)]
public class RequireOAuth2ScopeAttribute : RequestFilterAttribute {
private readonly string[] oauth2Scopes;
public RequireOAuth2ScopeAttribute(params string[] oauth2Scopes) {
this.oauth2Scopes = oauth2Scopes;
}
public override void Execute(IHttpRequest request, IHttpResponse response, object requestDto) {
try {
var authServerKeys = AppHostBase.Instance.Container.ResolveNamed<ICryptoKeyPair>("authServer");
var dataServerKeys = AppHostBase.Instance.Container.ResolveNamed<ICryptoKeyPair>("dataServer");
var tokenAnalyzer = new StandardAccessTokenAnalyzer(authServerKeys.PublicSigningKey, dataServerKeys.PrivateEncryptionKey);
var oauth2ResourceServer = new DotNetOpenAuth.OAuth2.ResourceServer(tokenAnalyzer);
var wrappedRequest = new HttpRequestWrapper((HttpRequest)request.OriginalRequest);
HttpContext.Current.User = oauth2ResourceServer.GetPrincipal(wrappedRequest, oauth2Scopes);
} catch (ProtocolFaultResponseException x) {
// see the GitHub project for detailed error-handling code
throw;
}
}
}
Second, this is how you hook into the ServiceStack HTTP client pipeline and use DotNetOpenAuth to add the OAuth2 Authorization: Bearer {key} token to the outgoing request:
// Create the ServiceStack API client and the request DTO
var apiClient = new JsonServiceClient("http://api.mysite.com/");
var apiRequestDto = new Shortlists { Name = "dylan" };
// Wire up the ServiceStack client filter so that DotNetOpenAuth can
// add the authorization header before the request is sent
// to the API server
apiClient.LocalHttpWebRequestFilter = request => {
// This is the magic line that makes all the client-side magic work :)
ClientBase.AuthorizeRequest(request, accessTokenTextBox.Text);
}
// Send the API request and dump the response to our output TextBox
var helloResponseDto = apiClient.Get(apiRequestDto);
Console.WriteLine(helloResponseDto.Result);
Authorized requests will succeed; requests with a missing token, expired token or insufficient scope will raise a WebServiceException
This is still very much proof-of-concept stuff, but seems to work pretty well. I'd welcome feedback from anyone who knows ServiceStack or DotNetOpenAuth better than I do.
Update
On further reflection, your initial thought, to create a RequiredScope attribute would be a cleaner way to go. Adding it to the ServiceStack pipeline is as easy as adding the IHasRequestFilter interface, implementing a custom request filter, as documented here: https://github.com/ServiceStack/ServiceStack/wiki/Filter-attributes
public class RequireScopeAttribute : Attribute, IHasRequestFilter {
public void RequireScope(IHttpRequest req, IHttpResponse res, object requestDto)
{
//This code is executed before the service
//Close the request if user lacks required scope
}
...
}
Then decorate your DTO's or Services as you've outlined:
using ServiceStack.ServiceInterface;
using SpotAuth.Common.ServiceModel;
namespace SpotAuth.ResourceServer.Services {
[RequireScope("hello")]
public class HelloService : Service {
public object Any(Hello request) {
return new HelloResponse { Result = "Hello, " + request.Name };
}
}
}
Your RequireScope custom filter would be almost identical to ServiceStack's RequiredRoleAttribute implementation., so use it as a starting point to code from.
Alternately, you could map scope to permission. Then decorate your DTO or service accordingly (see SS wiki for details) for example:
[Authenticate]
[RequiredPermission("Hello")]
public class HelloService : Service {
public object Any(Hello request) {
return new HelloResponse { Result = "Hello, " + request.Name };
}
}
Normally ServiceStack calls the method bool HasPermission(string permission) in IAuthSession. This method checks if the list List Permissions in IAuthSession contains the required permission, so, in a custom IAuthSession you could override HasPermission and put your OAuth2 scopes checking there.

Cross platform authentication using ASP.NET Web API

How do I even begin coding authentication using ASP.NET Web API so it is cross-platform to support desktop, mobile and web? I'd read of some methods of doing RESTful authentication, such as using tokens in the header.
Are there any example projects out there that utilizes this method?
Questions:
If not how do I fix the [Authorize] attribute to read the token?
How do I generate this token? I dont think i can use formsauthentication because that uses cookies.
How do I handle the actual authorization, do the client send raw password and username then I generate the token or is there some other way?
How do I handle when my website is using it? I heard this is handled differently than when an app is using it, such as getting the domain and authorizing it.
I think tokens would be a solid way to go. Forms authentication is based on cookies for the web. Not the most idea situation for all non browser clients though.
What I'd suggest is creating a custom AuthorizationFilterAttribute and overriding the OnAuthorization method. In that method, you could check for the existence of a token that you've issued to the client after they've supplied valid credentials. You can use this attribute on any method or controller you want validated. Here's a sample you might reference
public class AuthorizeTokenAttribute : AuthorizationFilterAttribute
{
public override void OnAuthorization(HttpActionContext actionContext)
{
if (actionContext != null)
{
if (!AuthorizeRequest(actionContext.ControllerContext.Request))
{
actionContext.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized) { RequestMessage = actionContext.ControllerContext.Request };
}
return;
}
}
private bool AuthorizeRequest(System.Net.Http.HttpRequestMessage request)
{
bool authorized = false;
if (request.Headers.Contains(Constants.TOKEN_HEADER))
{
var tokenValue = request.Headers.GetValues("TOKEN_HEADER");
if (tokenValue.Count() == 1) {
var value = tokenValue.FirstOrDefault();
//Token validation logic here
//set authorized variable accordingly
}
}
return authorized;
} }
TOKEN_HEADER is just a string representing an HTTP header that the client should pass back for authenticated requests.
So let's walk through it
Client requests secure data
Client is not authorized, return a response with an Unauthorized status code
Client sends credentials to authenticate, which should be secured via HTTPS
Once validated, client receives a token via an HTTP header, or whatever works for you
Client tries requesting secure data again, this time attached the token to the request
The AuthorizeTokenAttribute will validate the token and allow the action to execute.
Also, check this post by John Petersen. Making your ASP.NET Web API’s secure
There are lots of ways to authenticate users for a REST service. Using tokens is possible but just using Basic Authentication is even simpler and about as standard and cross platform as you can go.
Don't confuse authorization with authentication. The [Authorize] attribute is all about authorization but only after a user has been authenticated using some other mechanism. Authorization is completely useless without doing proper authentication first.
The best resource to check is Dominick Baier who is an expert on the subject.
I use a very simple approach:
define an access profile with its unique accessId and accessKey (e.g. MD5 hashed GUID value)
store such access profile in database
every request (GET/POST/etc.) must supply accessId, queryHash (MD5 hash value represents the query) and signature (MD5 hash value of queryHash + accessKey). Of course the client needs keep the accessKey in a secure place!!!
server gets the request will check the accessId and the signature using the same calculation algorithm to reject or grant the access (authenticate)
further authorization can be done on request type basis utilizing the access profile
the service with this approach using the new ASP.NET MVC web API can serve whatever type of client: browser/javascript and native(desktop or mobile) etc.
U can use ActionFilterAttribute and override the OnActionExecuting method.
Later on register this filter in global.cs to apply this filter for all the actions like this in Application Start method
var config = GlobalConfiguration.Configuration;
config.Filters.Add(new CustomAuthAttribute ());
{
namespace Customss
{
Public class CustomAuthAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
// To inforce HTTPS if desired , else comment out the code
if (!String.Equals(actionContext.Request.RequestUri.Scheme, "https", StringComparison.OrdinalIgnoreCase))
{
actionContext.Response = new HttpResponseMessage(System.Net.HttpStatusCode.BadRequest)
{
Content = new StringContent("HTTPS Required")
};
return;
}
// get toekn from the header
var userToken = actionContext.Request.Headers.GetValues("UserToken");
// Customer Logic to check the validity of the token.
// U can have some DB logic to check , custom STS behind or some loca cache used to compare the values
}
}
}
}

Resources