Building a url to a STS in a passive scenario - asp.net-mvc

I'm used to program with WIF and the pattern usually goes like this:
- Add a STS Ref
- Set the location of the web that needs to be autorized (or decorate actions in MVC with [Autorize]
- if the user access the "reserved" section, they are redirected to the STS
But let's say I need to actually build a web page that offer the user a choice of STS in order to logon. Not by accessing a page that triggers the redirection by configuration, but by actively loggin in on the page.
If I do this, what would be the form of the url to my STS ? What are the params I need to set to make the user login then redirect to let'S say the index page ?

WIF can help you with this.
Here's a code snippet of what it looks like:
WSFederationAuthenticationModule fam = FederatedAuthentication.WSFederationAuthenticationModule;
var signInRequest = new SignInRequestMessage(new Uri(fam.Issuer), fam.Realm)
{
AuthenticationType = fam.AuthenticationType,
Freshness = fam.Freshness,
Realm = "some realm",
Context = GetAReturnUrl(),
HomeRealm = "A Home Realm"
};
Then you can retrieve the URL with:
signInRequest.WriteQueryString()

Related

How To Add Custom Parameters to Identity Server LogoutRequest?

I have a legacy ASP.NET MVC (Owin) website using the latest OpenIdConnect library, and .NET Core 3.1 MVC Identity Server app running the latest Identity Server package.
I need to pass a custom parameter to Identity Server at logout (the use case is that I need to display a different "logout reason" message depending on how the logout was initiated on the client). So I'm intercepting the OpenIdRequestType.Logout notification setting a custom parameter via n.ProtocolMessage.SetParameter("some_key", "SomeValue") and I can see that the Parameters dictionary has the new value in it. But when the Logout post to my Identity Server comes in, the LogoutRequest.Parameters collection is empty.
Notifications = new OpenIdConnectAuthenticationNotifications
{
RedirectToIdentityProvider = async n =>
{
switch (n.ProtocolMessage.RequestType)
{
case OpenIdConnectRequestType.Logout:
n.ProtocolMessage.SetParameter("some_key", "SomeValue");
break;
...
On the Identity Server side, I'm calling
var logout = await _interaction.GetLogoutContextAsync(logoutId);
and finding that logout.Parameters is empty.
I found this SO question that suggests that the oidc-client javascript library can handle adding additional parameters that will show up in the LogoutRequest object so I'm guessing it's possible and I'm just missing something simple. Any ideas would be appreciated.

ASP.Net MVC application access to other servers

We have an ASP.Net MVC application (website), just a straight web app. The URL it needs to connect to is an ASP.Net WebAPI2 web service on port 14015. The MVC application is calling the Web service anonymously using a WebClient class; the web service is secured by limiting which IPs can connect to it. There is no authorization mode to access except by IP.
using (WebClient client = new WebClient())
{
//****************************
// We make the web service call like this:
//****************************
string url = #"http://secure.example.com:14015/lms/SSOKey/1158341";
string key = client.DownloadString(url);
//****************************
// Then we append the returned key to build the full URL. This URL is used
// in the View to build a link button.
//****************************
string login_url = #"http://192.168.1.1/tc/login.do?uid=" + key;
login_url = login_url.Replace("\"", string.Empty);
//****************************
// Pass the URL to the view to build the link button
//****************************
ViewBag.LoginURL = login_url;
}
I can access the URL from a browser on the server where the MVC application is published, however, the call is unsuccessful. Any ideas how I may find out why this won't connect??
If you are calling the code inside of a controller and the users have to logon to your web app, than this code should give you what you need:
string currentUser = User.Identity.Name;
If your users use your web app anonymously currentUser will be blank.
Adding this code solved the problem.
System.Net.ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
Since we limit access by IP Addresses, very low security risk.

How to add parameters to redirect_uri in WebApi Oauth Owin authentication process?

I'm creating a webapi project with oauth bearer token authenthication and external login providers (google, twitter, facebook etc.). I started with the basic VS 2013 template and got everything to work fine!
However, after a user successfully logs is, the owin infrastructure creates a redirect with the folllowing structure:
http://some.url/#access_token=<the access token>&token_type=bearer&expires_in=1209600
In my server code I want to add an additional parameter to this redirect because in the registration process of my app, a new user needs to first confirm and accept the usage license before he/she is registered as a user. Therefore I want to add the parameter "requiresConfirmation=true" to the redirect. However, I've no clue about how to do this. I tried setting AuthenticationResponseChallenge.Properties.RedirectUri of the AuthenticationManager but this doesn't seem to have any affect.
Any suggestions would be greatly appreciated!
It should be relatively easy with the AuthorizationEndpointResponse notification:
In your custom OAuthAuthorizationServerProvider implementation, simply override AuthorizationEndpointResponse to extract your extra parameter from the ambient response grant, which is created when you call IOwinContext.Authentication.SignIn(properties, identity).
You can then add a custom requiresConfirmation parameter to AdditionalResponseParameters: it will be automatically added to the callback URL (i.e in the fragment when using the implicit flow):
public override Task AuthorizationEndpointResponse(OAuthAuthorizationEndpointResponseContext context) {
var requiresConfirmation = bool.Parse(context.OwinContext.Authentication.AuthenticationResponseGrant.Properties.Dictionary["requiresConfirmation"]);
if (requiresConfirmation) {
context.AdditionalResponseParameters.Add("requiresConfirmation", true);
}
return Task.FromResult<object>(null);
}
In your code calling SignIn, determine whether the user is registered or not and add requiresConfirmation to the AuthenticationProperties container:
var properties = new AuthenticationProperties();
properties.Dictionary.Add("requiresConfirmation", "true"/"false");
context.Authentication.SignIn(properties, identity);
Feel free to ping me if you need more details.

ldap.rememberMe.usernameMapper.userDnBase (multiple instances of?) (searchSubtree search capability)

I have a Grails application that is successfully using the latest spring-security-core:2.0-RC4 and spring-security-ldap:2.0-RC2. Users can login perfectly using
grails.plugin.springsecurity.ldap.search.base setting for LDAP login authentication.
There is a different setting for the rememberMe userDnBase (mapper) and that setting is:
grails.plugin.springsecurity.ldap.rememberMe.usernameMapper.userDnBase
The LDAP authentication grails.plugin.springsecurity.ldap.search.base is set to ou=people,dc=sitcudy,dc=edu. As mentioned above - the logins work fine because there is a property called searchSubtree that I have set to true. Unfortunately, the searchSubtree setting does not hold true and carry through consistently within the 'remember-me' portion of the code (.ldap.rememberMe)*. The remember-me portion of the code uses a map for the base DN, grails.plugin.springsecurity.ldap.rememberMe.usernameMapper.userDnBase
so I put in a string in the config.groovy file (the same as for the authentication piece) to map to the base DN of ou=people,dc=sitcudy,dc=edu.... which gets mapped to the DN for the LDAP user look up upon returning to the application for persistence cookie login.
Here's where my problem comes in, most users are segregated into different DIT's in our LDAP system. For example, some uses are in ou=staff,ou=people,dc=sitcudy,dc=edu while other users are in ou=students,ou=people,dc=sitcudy,dc=edu therefore, because of the remember me mapping, upon returning to the application, once verifying the cookie, the code tries to bind users in this format, uid=reuben_marcus,ou=people,dc=sitcudy,dc=edu which doesn't exist. What does exist is uid=reuben_marcus,ou=staff,ou=people,dc=sitcudy,dc=edu therefore the cookie is destroyed and the login (IS_AUTHENTICATED_REMEMBERED) never occurs.
If I change grails.plugin.springsecurity.ldap.rememberMe.usernameMapper.userDnBase
to ou=staff,ou=people,dc=sitcudy,dc=edu the remember me functionality works perfect for all staff members, but it doesn't work for all other people - students, faculty etc.
The main setting in question below for me in this issue is:
grails.plugin.springsecurity.ldap.rememberMe.usernameMapper.userDnBase
Since this is just a mapping and there isn't allowance for multiple userDNBases or searchSubtree search.. How is the ‘remember-me’ code supposed to find users that do not fall into this base DN setting...??
I wonder if I'm doing something wrong or if this is a feature request to have the ‘remember me’ code have options for multiple mapping userDNBases or allow it to have a searchSubtree search capability.
Relevant settings from my config.groovy:
grails.plugin.springsecurity.ldap.mapper.roleAttributes = 'sitPriRole,uid'
grails.plugin.springsecurity.ldap.context.managerDn = 'uid=SPS_bind,ou=People,dc=sitcudy,dc=edu'
grails.plugin.springsecurity.ldap.context.managerPassword = 'xxx'
grails.plugin.springsecurity.ldap.context.server = 'ldap://ds01.sitcudy.edu:389'
grails.plugin.springsecurity.ldap.authorities.groupSearchBase ='ou=Groups,dc=sitcudy,dc=edu'
grails.plugin.springsecurity.ldap.search.base = 'ou=People,dc=sitcudy,dc=edu'
grails.plugin.springsecurity.ldap.search.searchSubtree = true
grails.plugin.springsecurity.ldap.auth.hideUserNotFoundExceptions = false
grails.plugin.springsecurity.ldap.search.attributesToReturn = ['uid', 'sitPriRole', 'mail', 'displayName']
grails.plugin.springsecurity.providerNames = ['ldapAuthProvider', 'anonymousAuthenticationProvider', 'rememberMeAuthenticationProvider']
grails.plugin.springsecurity.ldap.authorities.retrieveGroupRoles = false
grails.plugin.springsecurity.ldap.authorities.retrieveDatabaseRoles = false
grails.plugin.springsecurity.password.algorithm = 'SHA-256'
grails.plugin.springsecurity.rememberMe.persistent = true
grails.plugin.springsecurity.rememberMe.persistentToken.domainClassName = 'od.PersistentLogin'
// role-specific LDAP config
// grails.plugin.springsecurity.ldap.useRememberMe = true
grails.plugin.springsecurity.ldap.rememberMe.detailsManager.attributesToRetrieve = null
grails.plugin.springsecurity.ldap.rememberMe.detailsManager.groupMemberAttributeName = 'uniquemember'
grails.plugin.springsecurity.ldap.rememberMe.detailsManager.groupRoleAttributeName = 'cn'
grails.plugin.springsecurity.ldap.rememberMe.detailsManager.groupSearchBase = 'ou=Groups,dc=sitcudy,dc=edu'
grails.plugin.springsecurity.ldap.rememberMe.detailsManager.passwordAttributeName = 'userPassword'
grails.plugin.springsecurity.ldap.rememberMe.usernameMapper.userDnBase = 'ou=People,dc=sitcudy,dc=edu'
grails.plugin.springsecurity.ldap.rememberMe.usernameMapper.usernameAttribute = 'uid'
This problem was mentioned here: Grails - Spring security plugin ldap: remember me not working
I found a workaround for this by registering custom TokenBasedRememberMeServices bean in resources.groovy.
I didn't use persistent logins functionality available in grails-spring-security-ldap plugin, because I found it incompatible with my Active Directory tree layout. Most probably, this could be customized by extending LdapUserDetailsManager but in my situation I found it unnecessary to store token in database.
I used regular spring security remember me cookie option but without storing user password in the cookie. I extended the following methods from TokenBasedRememberMeServices
makeTokenSignature - make token signature without password field
processAutoLoginCookie- if cookie exists, then retrieve username from cookie token and fetch ldap user details (I had to write my own method retrieveUserFromLdap() explained later)
onLoginSuccess - this gets triggered when user logs in with remember-me option checked. Here, I'm removing password and saving token signature to cookie.
To fetch user details and roles from LDAP it might depend on specific implementation but my method looks like this:
static protected UserDetails retrieveUserFromLdap(String username) {
def ldapUserSearch = Holders.applicationContext.getBean('ldapUserSearch')
def userContextMapper = Holders.applicationContext.getBean('ldapUserDetailsMapper')
def authoritiesPopulator = Holders.applicationContext.getBean('ldapAuthoritiesPopulator')
def userContext = ldapUserSearch.searchForUser(username)
def userAuthorities = authoritiesPopulator.getGrantedAuthorities(userContext,username)
userContextMapper.mapUserFromContext(userContext,username,userAuthorities)
}

URL encoding # sharp to Durandal SPA

I know it is not necessary to specify a login URL to a Durandal login page. But I wonder how to fix the following problem I'm facing to redirect a Authentication to a specific Durandal Page that has a Sharp sign (i.e. #).
public void ConfigureAuth(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
//LoginPath = new PathString("/" + HttpUtility.UrlDecode("#") + "/login")
LoginPath = new PathString("/#/login")
});
...
}
When I paste:
http://localhost/#/login
to a browser URL I can navigate to the login page without any problems. I can login and it is working fine.
Because I'm mixing MVC with SPA in some scenarios, when I add [Authorize] attribute to an MVC controller, then I will be redirected as expected to
http://localhost/%23/login?ReturnUrl=%2Fe
and I get the error:
Server Error in '/' Application.
The resource cannot be found.
Description: HTTP 404. The resource you are looking for (or one of its dependencies) could have been removed, had its name changed, or is temporarily unavailable. Please review the following URL and make sure that it is spelled correctly.
Requested URL: /#/login
how to use the # sign instead of %23 char encoding? or maybe I'm messing something else!!!
FYI:
My question is related to character encoding not something line this: Login page on different domain because I may face the same problem in other situations in the future.
As far as I know it isn't possible to use a # from the server. A possible solution could be to redirect the user from the shell in durandal.js when the user isn't authenticated.
I don't know if it is too late in the life cycle but you could try to catch the redirect in the global asax file in the Application_EndRequest event and fix the response. I have code like this to change the actionresult to a jsonresult that my js can work with so that when a user attempts to reach an MVC controller resource that I have protected via java script ajax I can handle it properly.
protected void Application_EndRequest()
{
var context = new HttpContextWrapper(this.Context);
// If we're an ajax request and forms authentication caused a 302,
// then we actually need to do a 401
if (FormsAuthentication.IsEnabled && context.Response.StatusCode == 302
&& context.Request.IsAjaxRequest())
{
var jsRet = new JSONFunctionResult<Boolean>();
if (this.Request.IsAuthenticated)
{
jsRet.Messages.Add(new JSONFunctionResultMessage()
{
Text = string.Format("You must have one of these roles to perform the operation: {0}", context.Response.Headers["RolesRequired"]),
Title = "Security Violation",
Type = (int)JSONFunctionResultMessageTypes.authorization
});
}
else
{
jsRet.Messages.Add(new JSONFunctionResultMessage()
{
Text = "You must be logged into to access this resource",
Title = "Security Violation",
Type = (int)JSONFunctionResultMessageTypes.authentication
});
}
jsRet.OperationStatus = JSONFunctionResultOperationStatus.error.ToString();
string jresponse = JsonConvert.SerializeObject(jsRet);
context.Response.Clear();
context.Response.StatusCode = 200;
context.Response.ContentType = "application/json";
context.Response.Write(jresponse);
}
}
In my code I check for a redirect and if forms auth is enabled and if the user is Authenticated and change the response accordingly. For your code you could maybe change the response to go to your correct login page?

Resources