StarterSTS as a backup authentication store - single-sign-on

I have setup startersts as the ClaimsProviderTrust in ADFS 2.0. Configured the login form to be displayed by changing the ADFS 2.0 web.config entry. I am trying to customize the login process here such that when some one enter's email address he will be logging into StarterSts otherwise through Active Directory which is provided by the ADFS 2.0 Installation. I had tried the code in the post http://blogs.msdn.com/b/card/archive/2010/01/27/customizing-the-ad-fs-2-0-sign-in-web-pages.aspx
protected void SubmitButton_Click( object sender, EventArgs e )
{
try
{
SignInWithTokenFromOtherSTS( UsernameTextBox.Text, PasswordTextBox.Text );
}
catch ( Exception ex )
{
//
// Fall back to signing in locally with the given username and password.
//
SignIn( UsernameTextBox.Text, PasswordTextBox.Text );
}
}
I just don't know what is the values for variables OtherSTSAddress and YourSTSAddress. Is there any more configuration i need to do after making this address correct

As per the link you provided:
const string OtherSTSAddress = "https://ipsts.federatedidentity.net/SecurityTokenService/InteropSts.svc/Sts";
const string YourSTSAddress = "https://your-sts/adfs/ls/";
So the former is the WS-Trust endpoint of StarterSTS.
The latter is the ADFS WS-Fed endpoint. Just substitute the URL of the box where you installed ADFS in the "your-sts" section.
According to the article, that's all you need.

Related

Keycloak integration to external identity provider fails when validation tokens with JWKS URL

I'm configuring an external identity provider in my Keycloak instance and trying to get it to validate the tokens using a external JWKS URL. Using the converted PEM from JWKS works fine, the using the URL is not working.
The token validation fails upon login with the following message:
[org.keycloak.broker.oidc.AbstractOAuth2IdentityProvider] (default task-4) Failed to make identity provider oauth callback: org.keycloak.broker.provider.IdentityBrokerException: token signature validation failed
I debugged the Keycloak server get more on the problem and found a "problem" in class JWKSUtils:
/**
* #author Marek Posolda
*/
public class JWKSUtils {
//...
public static Map<String, KeyWrapper> getKeyWrappersForUse(JSONWebKeySet keySet, JWK.Use requestedUse) {
Map<String, KeyWrapper> result = new HashMap<>();
for (JWK jwk : keySet.getKeys()) {
JWKParser parser = JWKParser.create(jwk);
if (jwk.getPublicKeyUse().equals(requestedUse.asString()) && parser.isKeyTypeSupported(jwk.getKeyType())) {
KeyWrapper keyWrapper = new KeyWrapper();
keyWrapper.setKid(jwk.getKeyId());
keyWrapper.setAlgorithm(jwk.getAlgorithm());
keyWrapper.setType(jwk.getKeyType());
keyWrapper.setUse(getKeyUse(jwk.getPublicKeyUse()));
keyWrapper.setVerifyKey(parser.toPublicKey());
result.put(keyWrapper.getKid(), keyWrapper);
}
}
return result;
}
//...
}
The if fails with a NullPointerException because the call jwk.getPublicKeyUse() returns null.
But I found out that it's null because the JWKS URL returns a single key without the attribute use, which is optional according to the specification. [https://www.rfc-editor.org/rfc/rfc7517#section-4.2]
Keycloak only accepts JWKS URLs that return all keys with the attribute use defined. But the IdP I'm trying to connect does not return that attribute in the key.
Given that situation, to who should I file an issue, the IdP or to Keycloak? Or is there something I'm doing wrong in the configuration?
I filed an issue with Keycloak about this exact problem in August 2019.
Their answer:
Consuming keys without validating alg and use is dangerous as such
Keycloak requires these to be present.
In my case, I contacted the IdP and they were able to populate the "use" parameter. If that is not an option, then you're pretty much stuck with your workaround.

Keycloak Custom Validation Output messages

I'm using jboss keycloak 1.5 final version.
I developed my custom user federation provider interfacing with keycloak properties and my user enterprise database.
My need is to send up to user the login interface custom error messages based on particular specific error related to my legacy user db.
I saw keycloak themes have a resources folder by which i can localize and add new messages. Then i can reference them by angular js using
$myMessage
notation. The problem is i want to rise up a message from keycloak server. My user federation provider implements UserFederationProvider interface. So i should have to override:
#Override
public CredentialValidationOutput validCredentials(RealmModel realm, UserCredentialModel credential) {
LOGGER.info("validCredentials(realm, credential)");
return CredentialValidationOutput.failed();
}
which seems to be the method i was looking for just because CredentialValidationOutput contains custom messages to be sent as validation output. The problem is this method is never called.
Why?
I'll post the answer found on my own.
It's necessary to develop your own Authenticator. For example refer to Keycloak UsernameAndForm and UsernameAndFormFactory implementation.
You can find them on Keycloak github source code:
https://github.com/keycloak/keycloak/tree/master/services/src/main/java/org/keycloak/authentication/authenticators/browser
The main validation method are:
public boolean validateUserAndPassword(AuthenticationFlowContext context, MultivaluedMap<String, String> inputData) {
...
}
public boolean validatePassword(AuthenticationFlowContext context, UserModel user, MultivaluedMap<String, String> inputData) {
...
}
From your custom user federation provider you can throw your custom exception and catch them in the two methods above adding:
catch (YourCustomException ex){
...
Response challengeResponse = context.form()
.setError("YOUR ERROR MESSAGE", me.getMandator()).createLogin();
context.failureChallenge(AuthenticationFlowError.INVALID_USER, challengeResponse);
return false;
}
Of course in your project you have to add
META-INF/service/org.keycloak.authentication.AuthenticatorFactory
In which you specify the full qualified name of your AuthenticatorFactory.
For a valid guide make reference to Keycloak User Guide 1.6.1 Final. Chapter 33.3

Storing User Credientials in the Bayeux Server

I'd like to store the clients UserName and SessionId when a client subscribes to a particular channel. When i override canHandshake() i can get the user credentials using the following:
userName = (String) authentication.get("userName");
sessionId = (String) authentication.get("sessionId");
Just wondering how i can store these credentials and later retrieve them? I've had a look at the authentication documentation here and it just mentions linking the authentication data to the session. Is this the Bayeux Server side session??
Thanks
The "linking" can be done in several ways.
You can link this information in an external map via:
#Override
public boolean canHandshake(BayeuxServer server, ServerSession session, ServerMessage message)
{
...
Map<String, Object> authentication = ...;
map.put((String)authentication.get("userName"), session);
...
}
where the map can be a java.util.ConcurrentHashMap<String, ServerSession> field in the security policy itself, or in another object such as a user service.
For simpler use cases, the userName can be linked directly to the session in this way:
session.setAttribute("userName", authentication.get("userName"));
Or you can use both techniques.
This is the updated link for the authentication how-to, and you can find the latest comprehensive CometD documentation at http://docs.cometd.org.

Jira 5.2 Seraph SSO Login behind reverse proxy

Since a few days I'm trying to enable SSO for Jira 5.2 and figured out, that the help page from Jira is outdated.
Each example uses an old version of atlassian-seraph (Jira 5.2 uses 2.6.0).
Goal:
I want to get automatically logged in into Jira if I'm logged in into Webseal (reverse proxy).
Background:
Jira is behind a reverse proxy (see picture).
This proxy authentificatates the user and holds the session.
If I'm logged in I want to be logged in in Jira, too
The only information provided is the user name
Question:
How to write a custom login module that reads the username from http_header and authentificates the user?
Links:
https://confluence.atlassian.com/display/DEV/Single+Sign-on+Integration+with+JIRA+and+Confluence
http://docs.atlassian.com/atlassian-seraph/latest/sso.html
https://answers.atlassian.com/questions/23245/how-to-integrate-jira-with-my-company-s-sso
In the end i figured it out by myself:
You need a custom authenticator
public class MyCustomAuthenticator extends DefaultAuthenticator {
protected boolean authenticate(Principal user, String password)
throws AuthenticatorException {
return true;
}
protected Principal getUser(String username) {
return getCrowdService().getUser(username);
}
private CrowdService getCrowdService() {
return (CrowdService)ComponentManager.getComponent(CrowdService.class);
}
}
Add the MyCustomAuthenticator to seraph-config.xml
<authenticator class="com.company.jira.MyCustomAuthenticator"/>
Write a Custom Filter to set the user name from http-header
public class CustomFilter extends PasswordBasedLoginFilter {
#Override
protected UserPasswordPair extractUserPasswordPair(
HttpServletRequest request) {
String username = request.getHeader("iv-header");
if (username != null && username.trim().length() != 0) {
return new PasswordBasedLoginFilter.UserPasswordPair(
username, "DUMMY", false);
}
return null;
}
}
Replace the filter within the web.xml
<filter>
<filter-name>login</filter-name>
<filter-class>com.company.jira.CustomFilter</filter-class>
</filter>
These jar's are needed for Jira 5.2
embedded-crowd-api-2.6.2
jira-core-5.2.1
atlassian-seraph-2.6.0
I am not familiar with Jira authentication, but I do understand well the SiteMinder/ WebSeal authentication.
Both systems authenticate user and send the user name in an HTTP header.
The name of HTTP header can be configured. Also, they can send additional user properties, like the user email in the additional HTTP headers.
TO authenticate a user behind SiteMinder/ WebSeal it is just required to take the HTTP header and to create an application session using the user name from the header.
You definitely can solve it in Jira. You have 2 options:
To use already created SiteMinder authenticator:
https://confluence.atlassian.com/display/DEV/SiteMinder+Custom+Seraph+Authenticator+for+Confluence
The problem that I did not find how to configure the HTTP header name for the user name header. It assumes that the header name is uid
You need to configure the header uid in WebSeal or try to obtain sources and make the header name configurable.
Implement your own authenticator according to your link:
http://docs.atlassian.com/atlassian-seraph/latest/sso.html
Obtain the user name using the code
httpServletRequest.getHeader(userNameHeaderName);

ReportViewer control using ReportServerCredentials.NetworkCredentials

I'm using the ReportViewer control to access a SSRS 2008 Report from a VS2010 ASP.NET MVC 2 Web Application deployed on IIS7 with the following setup:
Forms Authentication and Anonymous Authentication enabled
ASP.NET Impersonation disabled
App pool identity configured to use local user account that has access rights to specific folders on the server required by the application
The webserver is not part of the domain but the SSRS server is on the domain
Since I need to use separate credentials to access SSRS Reports, I have implemented IReportServerCredentials as explained here:
http://msdn.microsoft.com/en-us/library/microsoft.reporting.webforms.ireportservercredentials(v=vs.100).aspx
The problem I'm having is it always goes to IReportServerCredentials.ImpersonationUser instead of IReportServerCredentials.NetworkCredentials where I need it to go because I'm retrieving the required credentials off web.config.
Maybe I'm missing something simple here but I've tried different combination of these settings and have had no luck. Any pointers on how I could get this working would be much appreciated!
My code:
[Serializable]
public sealed class MyReportServerCredentials :
IReportServerCredentials
{
public WindowsIdentity ImpersonationUser
{
get
{
// Use the default Windows user. Credentials will be
// provided by the NetworkCredentials property.
return null;
}
}
public ICredentials NetworkCredentials
{
get
{
// Read the user information from the Web.config file.
// By reading the information on demand instead of
// storing it, the credentials will not be stored in
// session, reducing the vulnerable surface area to the
// Web.config file, which can be secured with an ACL.
// User name
string userName =
ConfigurationManager.AppSettings
["MyReportViewerUser"];
if (string.IsNullOrEmpty(userName))
throw new Exception(
"Missing user name from web.config file");
// Password
string password =
ConfigurationManager.AppSettings
["MyReportViewerPassword"];
if (string.IsNullOrEmpty(password))
throw new Exception(
"Missing password from web.config file");
// Domain
string domain =
ConfigurationManager.AppSettings
["MyReportViewerDomain"];
if (string.IsNullOrEmpty(domain))
throw new Exception(
"Missing domain from web.config file");
return new NetworkCredential(userName, password, domain);
}
}
public bool GetFormsCredentials(out Cookie authCookie,
out string userName, out string password,
out string authority)
{
authCookie = null;
userName = null;
password = null;
authority = null;
// Not using form credentials
return false;
}
}
this.MyReportView.ServerReport.ReportServerCredentials = new MyReportServerCredentials();
Thanks
I implement this same class on every reporting page I need and it works correctly.
Check your web.config file.
I have:
<configuration>
<system.web>
<authentication mode="Windows"/>
</configuration>
</system.web>
Yes. Authentication mode "Windows".
However, I have worked also using Forms Authentication.
So I think this is more a permissions issue.
Check this post:
UAC Windows 7 (Proffesional) 64 bit and SSRS 2008 R2 (10.50.1617) 64 bit
Also this post:
Reporting Services 2008: asking for username and password