We have integrated shibboleth web sso into our application to authenticate the user, Now we want to
do authorization for our application. The below is the process which is i am thinking for authz.
According to shibboleth idp, the unauthenticated user is redirects to login.jsp from idp
Once the user enters the username and password, the page is going to our database
and authenticates the user is valid or not.
Here i want to get the permissions for the user if he is authenticated.
Now again user redirects to idp with some information along with the permissions,
so idp redirects to our service provider with that permissions, no we can control the authorization
for the users.
Here i came to know that i have to deal with attribute-resolver.xml, right now we are using
principle and transientId in this xml. So i know i could get the requierd info(Permissions) from saml response
from shibboleth idp.
So Please tell me, how to deal with attribute-resolver.xml to add our permissions for authorization.
Imp question: What is the better process to do authorization using shibboleth?
Kindly look into the following flow which i am following...
Authentication flow with idp and we are writing our own SP.
1) The below encodeSaml request is going to Idp like following:
public Pair<String,String> getSAMLRequest(String spUrl, String consumerUrl) {
AuthnRequest authnRequest = null;
//String encodedSAMLRequest = null;
Pair<String,String> encodedSAMLRequest = null;
try {
authnRequest = this.buildAuthnRequestObject(spUrl, consumerUrl);
Encoder encoder = Encoder.getEncoder();
encodedSAMLRequest = encoder.encodeAuthnRequest(authnRequest);
} catch (MarshallingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return encodedSAMLRequest;
}
private AuthnRequest buildAuthnRequestObject(String spUrl,
String consumerUrl) {
Issuer issuer = getIssuer();
issuer.setValue(spUrl);
DateTime issueInstant = new org.joda.time.DateTime();
RequestedAuthnContext requestedAuthnContext = getRequestedAuthnContext();
AuthnRequest authRequest = getAuthnRequest(issueInstant, issuer,
consumerUrl, spUrl);
authRequest.setRequestedAuthnContext(requestedAuthnContext);
String systemTime = System.currentTimeMillis() + "";
authRequest.setID("SSOIDSAMLREQ" +systemTime);
authRequest.setVersion(SAMLVersion.VERSION_20);
authRequest.setAssertionConsumerServiceIndex(1);
return authRequest;
}
2) First time idp redirects the user to login.jsp by using configuration which is in the handler.xml using externalAuth
<ph:LoginHandler xsi:type="ph:ExternalAuthn"
externalAuthnPath="/external/login"
supportsForcedAuthentication="true" >
<ph:AuthenticationMethod>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</ph:AuthenticationMethod>
</ph:LoginHandler>
-->Once it comes to the above mentioned path the user is able to see the login.jsp and user will enter the credentials and submitting to the our server to validate the user. So we will get the boolean variable whether the user is valid or not.
-> Once we got status from our server we are preparing the request and response like following which is to be send it to the idp again(AuthenticationEngine.returnToAuthenticationEngine(req,resp)).
request.setAttribute(globalStrings.getForceAuthn(), false);
Principal principal = new UsernamePrincipal(login.getAttributes());
Subject subj = new Subject();
subj.getPrincipals().add(principal);
request.setAttribute(LoginHandler.PRINCIPAL_KEY, principal);
request.setAttribute(LoginHandler.PRINCIPAL_NAME_KEY, personId);
request.setAttribute(LoginHandler.SUBJECT_KEY, subj);
request.setAttribute(globalStrings.getAuthnMethod(), this.authenticationMethod);
AuthenticationEngine.returnToAuthenticationEngine(request, response);
3) We mention in the attribute-resolver and attribute-filter for the attributes to be released to the SP like below
<resolver:AttributeDefinition id="principal" xsi:type="PrincipalName" xmlns="urn:mace:shibboleth:2.0:resolver:ad">
<resolver:AttributeEncoder xsi:type="enc:SAML2StringNameID" />
<resolver:AttributeEncoder xsi:type="SAML2Base64" xmlns="urn:mace:shibboleth:2.0:attribute:encoder"
name="ORG_ATTRIBUTE_64" />
<resolver:AttributeEncoder xsi:type="SAML2String" xmlns="urn:mace:shibboleth:2.0:attribute:encoder"
name="ORG_ATTRIBUTE" />
</resolver:AttributeDefinition>
4) So will get the released required attributes from the SP(SAML response) and do further processing(authorization).
Do you own and operate the IdP? If not, you don't have access to attribute-resolver.xml and must lookup attributes in your database when your application receives the principal data.
attribute-resolver.xml is how the IdP gets attributes that may be relevant to multiple applications. All attributes will be resolved even if your application is not allowed to receive a particular attribute. So if you do own the IdP, and think this attribute will be relevant, by all means, load it in the IdP and read it out when your application receives a SAML response from the IdP.
This is all a matter of design, and different designs will be better for different use cases. Also, the more complex the permissions data, the more likely your app should handle it.
Related
So i have a client which consumes an api. The API is secured with keycloak.
Users signs in normally, but i want to allow users to sign in user without having to go keycloak's login page with their social media accounts like facebook or google.
I need a rest API with an implementation of how to get a url generated so when user click on this url in a button, it will take the user to the respective social login page to login while keycloak still serves as the broker.
Below is my implementation, it generates a url alright but does not take the user to google page to login
This is a rest Controller
#Secured("permitAll")
#GetMapping(path = "/generator")
public String brokerGenerator(HttpServletRequest httpServletRequest) throws ServletException {
String provider = "google";
String authServerRootUrl = "http://localhost:8080/";
String realm = "realmName";
String clientId = "clientName";
String nonce = UUID.randomUUID().toString();
MessageDigest md = null;
try {
md = MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
String input = nonce + clientId + provider;
byte[] check = md.digest(input.getBytes(StandardCharsets.UTF_8));
String hash = Base64Url.encode(check);
httpServletRequest.getSession().setAttribute("hash", hash);
String redirectUri = "http://localhost:4200/dashboard";
return KeycloakUriBuilder.fromUri(authServerRootUrl)
.path("auth/realms/realmName/google/link")
.queryParam("nonce", nonce)
.queryParam("hash", hash)
.queryParam("client_id", clientId)
.queryParam("redirect_uri", redirectUri).build(realm, provider).toString();
}
Keycloak supports this out of the box. See https://www.keycloak.org/docs/latest/server_admin/index.html#_client_suggested_idp
OIDC applications can bypass the Keycloak login page by specifying a hint on which identity provider they want to use.
This is done by setting the kc_idp_hint query parameter in the Authorization Code Flow authorization endpoint.
UPDATE
In your case you should use normal Keycloak Auth Code Flow endpoint and in addition to the basic query params provide kc_idp_hint param. This way the user is redirected to Keycloak login page first then Keycloak redirects him to the chosen identity provider login page (google in your case).
Here is an example redirect URL:
https://keycloak-domain/realms/REALM_NAME/protocol/openid-connect/auth?client_id=CLIENT_ID&redirect_uri=REDIRECT_URI&state=STATE&response_type=code&scope=openid&nonce=NONCE&kc_idp_hint=google
Edit your code according this example:
return KeycloakUriBuilder.fromUri(authServerRootUrl)
.path("realms/realmName/protocol/openid-connect/auth") // Url changed
.queryParam("response_type", "code") // Autherization Code Flow
.queryParam("scope", "openid") // Add additional scopes if needed
.queryParam("kc_idp_hint", "google") // This should match IDP name registered in Keycloak
.queryParam("nonce", nonce)
.queryParam("hash", hash)
.queryParam("client_id", clientId)
.queryParam("redirect_uri", redirectUri).build(realm, provider).toString();
You can manually initiate Keycloak redirection for test. Start normal login flow and when you redirected to Keycloak login page do not enter credentials, instead add kc_idp_hint=google to the URL and hit ENTER. Then you will be redirected right to Google login page.
I've created webapp (not native) in Azure AD. I have java code (adal4j) that
acquire token using appId/appSecret credentials:
String clientId = "xxxxxxxxxxxxxxxxxxxxxx";
String clientSecret = "yyyyyyyyyyyyyyyyyyyyyy";
String resourceUrl = "https://graph.windows.net";
String authorityUrl = "https://login.microsoftonline.com/zzzzzzzzzzzzzzzz/oauth2/authorize";
ExecutorService executorService = Executors.newFixedThreadPool(4);
Optional<UserInfo> userInfo = Optional.empty();
try {
AuthenticationContext authContext = new AuthenticationContext(authorityUrl, false, executorService);
Future<AuthenticationResult> future = authContext.acquireToken(resourceUrl, new ClientCredential(clientId, clientSecret), null);
AuthenticationResult result = future.get();
}
Now I would like to check if specified user/password combination is in Azure AD and if yes then get First and Last name of this user.
Is it possible to do this usinq acquired token ? How to write such code using adal4j ?
It sounds like what you're really trying to do is sign in a user and get their first/last name. As the comment said, the pattern suggested is not a valid one and would represent a security issue. Additionally, the use for clientId and clientSecret is not exactly for user credentials, but for app credentials. This is used for flows without user interaction for service/api applications, and doesn't sound like what you'll want.
Now, to achieve this you'll be using the OpenID Connect protocol. To simplify what will happen, your app (upon user trying to sign in) will redirect to the Microsoft sign in page (https://login.microsoftonline.com), enter their credentials and fulfill any other authorization requirements, consent to your app, and then redirected back. When they come back, your app will receive an ID Token which can be validated and used to get information about the user that has just sign in. During this time, Azure AD / Microsoft will also set a cookie on the browser so the user will get SSO across their account.
In terms of how to achieve this, I recommend following the ADAL4J Code Sample. This will get your app an ID Token, and also an Access/Refresh token that you can use to call the Microsoft Graph API. This API can also get you information about the user (basic profile info), but also their Office365, Intune, and Windows data.
I am a reasonably experienced Java developer (4-5 years) but new to Azure AD and its capabilities, so I apologize in advance for a potentially basic question. I have struggled to find any Microsoft documentation or Stack Overflow questions covering this topic in Java (vast majority being in C#) and from my understanding, C# has more Azure AD libraries than Java, hence the solution in C# will not necessarily be the solution in Java.
I am trying to complete a authentication POC based on a scenario in-which there is an existing Azure AD system, full of users, that I want to leverage as an authentication point. My Java application will collect the users username and password (I understand this is deprecated and non-ideal, but for legacy reasons required) and makes a call using the Microsoft adal4j library to an Azure endpoint which I can get to successfully return a JWC access token (in addition to a refresh and ID token).
This is my existing code snippet that retrieves the JWC access token.
private static AuthenticationResult getAccessTokenFromUserCredentials(String username, String password, String
AUTHORITY, String CLIENT_ID) throws Exception {
AuthenticationContext context = null;
AuthenticationResult result = null;
ExecutorService service = null;
try {
service = Executors.newFixedThreadPool(1);
context = new AuthenticationContext(AUTHORITY, false, service);
Future<AuthenticationResult> future = context.acquireToken(
"https://graph.windows.net", CLIENT_ID, username, password,
null);
result = future.get();
} finally {
service.shutdown();
}
if (result == null) {
System.out.println("ex)");
}
return result;
}
public void azureAuthenticate(String authority, String clientID, String username, String password){
AuthenticationResult result = null;
try {
result = getAccessTokenFromUserCredentials(username, password, authority, clientID);
DecodedJWT accessToken = JWT.decode(result.getAccessToken());
//Want to verify the validity of this access token
} catch (Exception ex) {
ex.printStackTrace();
}
}
My code is largely based on this Microsoft documentation
After receiving the token, I need to be able to validate its authenticity (I understand the business logic side of confirming its claims, but I am confused as to how to verify the signature is legitimate).
Thank you in advance for any help, I am happy to provide any clarifications needed.
The access token from the Azure AD is a JSON Web Token(JWT) which is signed by Security Token Service in private key. A JWT token is a non-encrypted digitally signed JSON payload which contains different attributes (claims) to identify the user. The signature is the last part of the JWT and needs to be used for verification of the payload. This signature was generated with the algorithm described in the header(RS256 if issued from AAD) to prevent unauthorised access.Please refer to this document for more details about JWT token .
To validate signature , firstly we should retrieve and cache the singing tokens (public key) :1)The first call is to the discovery endpoint. It's URL is formed as '/.well-known/openid-configuration' .2) Then you will find lots of metadata here including the issuer value and the jwks_uri endpoint address to get the keys to validate the token's signature .
Token signing is implemented according to JSON Web Key spec. Using Key ID and X.509 certificate thumbprint values from the token's header (kid and x5t parameters respectively) and then find the appropriate public key in the obtained collection of keys to verify the signature. I am not familiar with java ,but you could refer to this thread which includes code sample for how to validate the signature in Java .
I have a web application that I deploy using JBoss 5.2. In order for a user to use the application, he/she must authenticate with an LDAP server (using simple authentication) with a username and password. This is all done through setting up the login-config.xml for JBoss and providing a <login-module> with our implementation.
The problem comes in here: After having logged in, I have a scenario that requires the user to provide a username & password when a particular action is performed (which I will also authenticate with the LDAP server). I want to be able to reuse the same mechanism that I use for authenticating the user into the web application.
My form to log in to the application posts to j_security_check so in accordance with this, I was trying to send a request to j_security_check but JBOSS returns a 404. From reading around a bit, I've gathered j_security_check cannot be accessed by any arbitrary request and must be in response to a challenged request to a secured resource.
So then, how can I authenticate the second set of credentials the user has provided with the same LDAP server?
EDIT:
To clarify, the question is how to send the user's credential inputs to the LDAP server for authentication. Grabbing the input from the user, etc. is all done. All that is left is to take this input and send it to the LDAP server and get the response (which is where I am stuck).
If it helps to mention, the login to the web application uses a custom class that extends UsernamePasswordLoginModule.
So, after lots of research, I ended up finding a solution for JBoss environments (which is what I'm using).
Once you capture the user's credentials, you send them to your server via a POST/GET and your server can perform the following to use whatever authentication policy you have configured (in login-config.xml) to verify the credentials:
WebAuthentication webAuthentication = new WebAuthentication();
boolean success = webAuthentication.login(username, password);
To expand on this, I was also able to check the user's role/group via the HttpServletRequest (which is passed into my server-side handler):
boolean userIsInRole = servletRequest.isUserInRole("nameOfGroup")
The spring security documentation explains it
Wanted to add another answer for JBoss 6.2+, where WebAuthentication no longer exists.
I've used the creation of a LoginContext to achieve the same result:
String SECURITY_DOMAIN_NAME = "ssd"; // the security domain's name from standalone.xml
String username = "user";
String password = "password";
LoginContext lc = null;
try {
lc = new LoginContext(SECURITY_DOMAIN_NAME, new UsernamePasswordHandler(username, password.toCharArray()));
lc.login();
// successful login
} catch (LoginException loginException) {
// failed login
}
And the use uf lc.getSubject().getPrincipals() to verify roles.
I am writing a web application and have just implemented that a user can sign in via Twitter, using spring-social-(core/twitter).
However, Twitter behaves strangely. After the initial authentication/authorization, every time I'm sending a user to Twitter for authentication, Twitter prompts to authorize my application again. I've looked into the connected Twitter profile. My app is there and authorized correctly (in my case for read access).
I don't have a case of requesting additional permissions. All my application needs is read access (the authorization dialog confirms this).
I am using the OAuth1Operations (returned by the TwitterConnectionFactory) to do the OAuth dance and save the resulting connection in a database. My front-end is written with Wicket 1.5.
I can work around this behavior by just re-authorizing my app again and again when I want to sign in via Twitter, but this is a big nuisance. Anyone knows what I'm missing here?
Here is my code:
TwitterConnectionFactory connectionFactory = (TwitterConnectionFactory) connectionFactoryLocator.getConnectionFactory(Twitter.class);
String callbackUrl = [...];
if (pageParameters.get("oauth_token").isNull() || pageParameters.get("oauth_verifier").isNull()) {
MultiValueMap<String, String> params = new LinkedMultiValueMap<String, String>();
params.add("x_auth_access_type", "read");
OAuthToken token = connectionFactory.getOAuthOperations().fetchRequestToken(callbackUrl, params);
String url = connectionFactory.getOAuthOperations().buildAuthorizeUrl(token.getValue(), OAuth1Parameters.NONE);
getSession().setAttribute("twitter_token", token);
setResponsePage(new RedirectPage(url));
} else {
String token = pageParameters.get("oauth_token").toString();
String verifier = pageParameters.get("oauth_verifier").toString();
OAuthToken previousToken = (OAuthToken) getSession().getAttribute("twitter_token");
if (previousToken.getValue().equals(token)) {
AuthorizedRequestToken authorizedRequestToken = new AuthorizedRequestToken(previousToken, verifier);
OAuthToken accessToken = connectionFactory.getOAuthOperations().exchangeForAccessToken(authorizedRequestToken, null);
Connection<Twitter> connection = connectionFactory.createConnection(accessToken);
}
}
I've found the solution! It is also detailed here: Simple Twitter Oauth authorization asking for credentials every time
The problem was that I specifically requested Twitter to authorize my app every time. Replacing:
String url = connectionFactory.getOAuthOperations().buildAuthorizeUrl(token.getValue(), OAuth1Parameters.NONE);
with
String url = connectionFactory.getOAuthOperations().buildAuthenticateUrl(token.getValue(), OAuth1Parameters.NONE);
solves the issue!
Calling the URL for authentication does only ask for authorization if the app hasn't been authorized yet.