Validating Azure AD Signature in Java - java

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 .

Related

Can't specify JWKSet URL when validating confidential token coming from Azure AD

Connecting to Azure AD v2.0 endpoint I cannot validate my token which jwt.io and jwt.ms declare as valid
A public token is no problem:
IDTokenValidator validator = new IDTokenValidator(issuer, clientId, JWSAlgorithm.RS256, jwkSetUrl);
IDTokenClaimsSet validatedClaimsSet;
try {
validatedClaimsSet = validator.validate(jwt, null);
} catch (BadJOSEException | JOSEException e) {
LOGGER.error(e.getMessage());
return;
}
but validating a token coming from a confidential client will throw the exception
Signed JWT rejected: Another algorithm expected, or no matching key(s) found
with the following code
IDTokenValidator validator = new IDTokenValidator(issuer, clientId, JWSAlgorithm.RS256, new Secret(authMethod.getSecret()));
No matching keys are found because (JWKMatcher.java:1258)
keytype does not match: OCT instead of RSA
use is null
ops is null
It seems an inadequate JWKSet is created from the secret in ImmutableSecret.java:47 but I seem to lack understanding as to how the secret can contain the necessary data found in the jwks_uri from Azure
Any advice would be appreciated
Validating an ID token works the same way for both public and confidential clients. So just get rid of the secret parameter and your code will work - using the token's signing public key.
The secret parameter is for JWTs that are signed with a symmetric algorithm such as HMACSHA256 - which is almost never a good idea. More about this topic in JWT Security Best Practices.

To sign into this application the account must be added to the <TenantID> directory

I am trying to authenticate and app and get bearer token for further use. I get the error which is title of this thread.
Another thread describes same thing except that my code is in Java. the workaround is to use certificate method.
"To sign into this application the account must be added to the domain.com directory"
Can someone please describe detailed steps for this workaround :- certificate method
Or how can i fix the below code with any other method
Or is any other method to achieve this whole task
Here is my code
private final static String AUTHORITY = "https://login.microsoftonline.com/<tenantId>/OAuth2/Authorize";
private final static String CLIENT_ID = "<Client_Id>";
private final static String CLIENT_SECRET = "<Secret>";
public static void main(String args[]) throws Exception {
try (BufferedReader br = new BufferedReader(new InputStreamReader(
System.in))) {
String username = CLIENT_ID;
String password = CLIENT_SECRET;
service = Executors.newFixedThreadPool(1);
context = new AuthenticationContext(AUTHORITY, false, service);
Future<AuthenticationResult> future = context.acquireToken(
"https://graph.microsoft.com", CLIENT_ID, username, password,
null);
result = future.get();
}
finally {
service.shutdown();
}
}
I have registered my app in AAD app registration. The Client_Id is App Id and Secret is a key in the above code
In case, you want to make the code work in it's current form without any workaround, check for following things -
Make sure you have the correct tenantId GUID specified in the first line of code.
Steps to get tenantid -
Login to Azure Portal, Navigate to your Azure AD, Go to properties like in screenshot below and Directory ID should give you the GUID.
private final static String AUTHORITY = "https://login.microsoftonline.com/<tenantId>/OAuth2/Authorize";
Make sure that Username you are using, is for a user that belongs to your AzureAD tenant.
One possible reason could be if you're using a Microsoft account like xyz#outlook.com or hotmail.com etc. Try using an account that is created in this Azure AD like xyz#yourtenantdomain.onmicrosoft.com or any other verified domain that your tenant uses.
Future<AuthenticationResult> future = context.acquireToken("https://graph.microsoft.com", CLIENT_ID, username, password, null);
If you want to use certificate credentials to authenticate your app, you could refer to this article.
Steps:
1.Uploading the certificate file
2.Updating the application manifest
Code sample(it uses C#, you could refer):
Authenticating to Azure AD in daemon apps with certificates
It also shows how you can create a self-signed certificate using the New-SelfSignedCertificate Powershell command. You can also take advantage and use the app creation scripts to create the certificates, compute the thumbprint, and so on.

How to check user credentials in AZURE AD when acquired token using appId/appSecret (adal4j)?

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.

How to generate a kerberos token without user input

I'm trying to generate a Kerberos client token in java, in order to send it in a header for a kerberized service.
For that I created a jaas login conf, and a keytab for my user.
I want to generate the token automatically without any user input. For now I managed to create a token but i'm being
prompted to enter the user's password, which is not what I want.
When I set the 'doNotPrompt' to 'true' (in the login.conf) I get an exception "unable to obtain password from user",
even though i'm specifying a keytab file.
I found very little code samples in the web, though eventually I used them. But I didn't find an answer to what i'm trying to do.
Does anyone know how to accomplish what I want?
My login.conf is:
com.sun.security.jgss.initiate {
com.sun.security.auth.module.Krb5LoginModule required storeKey="true"
principal="HTTP/MyComp#DOMAIN" useKeyTab="true"
keytab="c:\Users\me\Desktop\abc.keytab";
}
And my java code is:
String clientPrincipal = "HTTP/MyComp#DOMAIN";
String serverPrincipal = "HTTP/ServerComp#DOMAIN";
Oid oid = new Oid("1.2.840.113554.1.2.2");
try
{
GSSManager manager = GSSManager.getInstance();
GSSName gssUserName = manager.createName(clientPrincipal, GSSName.NT_USER_NAME, oid);
GSSCredential clientGSSCreds = manager.createCredential(gssUserName.canonicalize(oid),
GSSCredential.INDEFINITE_LIFETIME,
oid,
GSSCredential.INITIATE_ONLY);
GSSName gssServerName = manager.createName(serverPrincipal, GSSName.NT_USER_NAME);
GSSContext clientContext = manager.createContext(
gssServerName.canonicalize(oid),
oid,
clientGSSCreds,
GSSCredential.INITATE_ONLY);
clientContext.requestCredDeleg(true);
byte[] token = clientContext.initSecContext(token, 0, token.length);
}
catch (GSSException e){
e.printStackTrace();
}
A keytab file can contain keys for different users, so you need to tell your login.conf the principal whose key you'll be using. This principal name is arbitrary: it only needs to match the name you provided when you created the keytab (although I recommend using the actual UPN of the user).

Authorization using shibboleth sso

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.

Categories