Spring Cloud Gateway Azure User token verification - graph

For my backend of few microservices I have API gateway (Spring Cloud Gateway) where I wanna verify if azure token user send from frontend is valid befor routing microservice.
So far I get only 401 response whether I add valid token or not.
My security config class:
#EnableWebFluxSecurity
public class SecurityConfiguration {
#Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeExchange(exchanges -> exchanges
.anyExchange().authenticated()
)
.oauth2ResourceServer(oauth2 -> oauth2
.jwt(withDefaults())
);
return http.build();
}
}
Application.properties
spring.security.oauth2.resourceserver.jwt.issuer-uri=https://login.microsoftonline.com/{tenant_id}/v2.0
pom.xml
<dependency>
<groupId>com.azure.spring</groupId>
<artifactId>spring-cloud-azure-starter-active-directory-b2c</artifactId>
</dependency>
Somehow I cannot find any help for scenerio when I already have access token and only wanna validate it on gateway before passing to services.

If the login / authentication is through azure active directory, please check to add the following com.azure.spring : spring-cloud-azure-starter-active-directory maven dependency to the pom.xml file.
<dependency>
<groupId>com.azure.spring</groupId>
<artifactId>spring-cloud-azure-starter-active-directory</artifactId>
<version>4.3.0</version>
</dependency>
Also please make sure , that if the issuer after decoding the token
has v2 endpoint, accessTokenAcceptedVersion should be set to
2 otherwise it is to be set to 0 or 1
Also try to give some time and please check the decoded token has audience equal to clientId or appId uri of the application and if scopes are granted admin consent.
Also please check this SO reference

Related

Multiple sasl authentication mechanisms in Wildfly 24 with Elytron

I've set up Wildfly to use OAUTHBEARER auth mechanism for remote JNDI EJB lookups between my server and a desktop app, and it works great.
However, I also want to set up a simple .properties file based auth method to communicate with the server without a token to be able request one, or access information about the keycloak auth server as i don't want to hardcode it into my client application, and I can't seem to make it work. Whatever I do, the server rejects my auth attempts.
The sample code I was trying to reach the a pre auth bean with looks like this:
private static PreAuth lookUpPreAuthBean() throws NamingException
{
final Properties jndiProperties = new Properties();
jndiProperties.put(Context.INITIAL_CONTEXT_FACTORY, "org.wildfly.naming.client.WildFlyInitialContextFactory");
jndiProperties.put(Context.PROVIDER_URL,"http-remoting://localhost:8080");
jndiProperties.put(Context.SECURITY_PRINCIPAL, "test");
jndiProperties.put(Context.SECURITY_CREDENTIALS, "test");
final Context initialContext = new InitialContext(jndiProperties);
return (PreAuth) initialContext.lookup("ejb:server/server-ejb/PreAuthImpl!co.my.package.server.PreAuth");
}
Here is the stacktrace I get:
Suppressed: javax.security.sasl.SaslException: ELY05019: No token was given
at org.wildfly.security.mechanism.oauth2.OAuth2Client.getInitialResponse(OAuth2Client.java:66)
at org.wildfly.security.sasl.oauth2.OAuth2SaslClient.evaluateMessage(OAuth2SaslClient.java:62)
at org.wildfly.security.sasl.util.AbstractSaslParticipant.evaluateMessage(AbstractSaslParticipant.java:225)
at org.wildfly.security.sasl.util.AbstractSaslClient.evaluateChallenge(AbstractSaslClient.java:98)
at org.wildfly.security.sasl.util.AbstractDelegatingSaslClient.evaluateChallenge(AbstractDelegatingSaslClient.java:54)
at org.wildfly.security.sasl.util.PrivilegedSaslClient.lambda$evaluateChallenge$0(PrivilegedSaslClient.java:55)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at org.wildfly.security.sasl.util.PrivilegedSaslClient.evaluateChallenge(PrivilegedSaslClient.java:55)
at org.jboss.remoting3.remote.ClientConnectionOpenListener$Capabilities.lambda$handleEvent$1(ClientConnectionOpenListener.java:459)
at org.jboss.remoting3.EndpointImpl$TrackingExecutor.lambda$execute$0(EndpointImpl.java:991)
at org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
at org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:1990)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1486)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1348)
at org.xnio.XnioWorker$WorkerThreadFactory$1$1.run(XnioWorker.java:1280)
at java.base/java.lang.Thread.run(Thread.java:829)
Suppressed: javax.security.sasl.SaslException: DIGEST-MD5: Server rejected authentication
at org.jboss.remoting3.remote.ClientConnectionOpenListener$Authentication.handleEvent(ClientConnectionOpenListener.java:760)
at org.jboss.remoting3.remote.ClientConnectionOpenListener$Authentication.handleEvent(ClientConnectionOpenListener.java:602)
at org.xnio.ChannelListeners.invokeChannelListener(ChannelListeners.java:92)
at org.xnio.conduits.ReadReadyHandler$ChannelListenerHandler.readReady(ReadReadyHandler.java:66)
at org.xnio.nio.NioSocketConduit.handleReady(NioSocketConduit.java:89)
at org.xnio.nio.WorkerThread.run(WorkerThread.java:591)
If I was to supply a token, the bearer mechanism works, seems like for some reason the other mechanism is not authenticating
Here are the parts from the Wildfly 24 standalone.xml I've modified:
EJB subsystem:
...
<default-security-domain value="other"/>
<application-security-domains>
<application-security-domain name="other" security-domain="ApplicationDomain"/>
</application-security-domains>
...
Elytron subsystem:
I would like to use the PreAuthRealm for the simple auth.
<security-domains>
<security-domain name="ApplicationDomain" default-realm="JWTRealm" permission-mapper="default-permission-mapper">
<realm name="JWTRealm" role-decoder="jwt-to-roles"/>
<realm name="PreAuthRealm" role-decoder="groups-to-roles"/>
</security-domain>
...
<\security-domains>
I want to use the application-users.properties file to store the username and the password, these credentials were added to the store with the add-user.sh script
<security-realms>
<identity-realm name="local" identity="$local"/>
<properties-realm name="PreAuthRealm">
<users-properties path="application-users.properties" relative-to="jboss.server.config.dir" digest-realm-name="PreAuthRealm"/>
<groups-properties path="application-roles.properties" relative-to="jboss.server.config.dir"/>
</properties-realm>
<token-realm name="JWTRealm" principal-claim="*my claim*">
<jwt issuer="*my issuer*" audience="account">
<key kid="*my key id*" public-key="*my public key*"/>
</jwt>
</token-realm>
</security-realms>
The sasl authentication factory setup:
<sasl-authentication-factory name="application-sasl-authentication" sasl-server-factory="configured" security-domain="ApplicationDomain">
<mechanism-configuration>
<mechanism mechanism-name="OAUTHBEARER">
<mechanism-realm realm-name="JWTRealm"/>
</mechanism>
<mechanism mechanism-name="DIGEST-MD5">
<mechanism-realm realm-name="PreAuthRealm"/>
</mechanism>
</mechanism-configuration>
</sasl-authentication-factory>
Remoting subsystem:
<subsystem xmlns="urn:jboss:domain:remoting:4.0">
<http-connector name="http-remoting-connector" connector-ref="default" sasl-authentication-factory="application-sasl-authentication"/>
</subsystem>
The strange part is, if I remove the sasl-authentication-factory="application-sasl-authentication" from the Remoting subsystem, and put back the original security-realm="ApplicationRealm" the above code runs and it actually authenticates me from the .properties file, but then of course the bearer token part won't work, which is the whole point.
What am I missing with my setup here? Is it even possible what i want to achieve?
Not a solution, but a workaround. Managed to make it work by not naming the realm "PreAuthRealm" but naming it the default "ApplicationRealm", and for whatever reason setting this realm as the default realm in the security domain makes this, and the JWT realm, work. Setting the "JWTRealm" as default only makes the JWT realm work.

Apigee shared flow to validate token

I am using Apigee as gateway to our application. Several applications will hit Apigee and Apigee will in-turn route the request to backend servers. Every incoming request will have a JWT token.
I want Apigee to pass that token to a auth server and auth server will validate if the token is valid or not.
If token is invalid(if auth server return any status other then 200) , I want Apigee to return 403 error as response to request else pass the request to backend server.
How can I implement this kind of shared flow? Is it even possible with Apigee ? Is there any better way to achieve this?
You can do that.
Create a shared flow for Authentication/Authorization which includes ServiceCallout policy which will make a call to auth server.
Based on result for Unauthorized/Bad request you can raise a fault response with help of RaiseFault.
If the response is OK it will proceed smooth to backend.
Sample shared flow.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<SharedFlowBundle revision="1" name="Auth">
<Policies>
<Policy>AssignVariableJwks</Policy> <!-- Assign Input values if needed via AssignMessage policy -->
<Policy>RequestAuthServer</Policy> <!-- Extrnal auth server call using ServiceCallout policy -->
<Policy>TokenNotFoundValidation</Policy> <!-- Validate response and raise fault if needed using RaiseFault policy -->
</Policies>
<Resources/>
<Spec/>
<subType>SharedFlow</subType>
<SharedFlows>
<SharedFlow>default</SharedFlow>
</SharedFlows>
</SharedFlowBundle>
For above shared flow create & attach required policies with logic and you're good to go.

Using GraphServiceClient to retrieve group info of Azure Ad members

i want to retrieve all members of a Azure Ad group within my backend application. I followed the steps here and here and here is my code:
But I always get this error when using the method:
Microsoft.Graph.ServiceException: Code: generalException Message: An
error occurred sending the request.
---> Microsoft.Graph.Auth.AuthenticationException: Code:
authenticationChallengeRequired Message: Authentication challange is
required.
Can someone help me with that? I didn't find this specific error.
Obviously these parameters(clientId/tenantId/clientSecret/groupId) need to be replaced with specific strings.
You could find clientId and tenantId via App registrations-> Overview:
clientSecret via App registrations-> Certificates & secrets:
groupId via Azure Active Directory -> Groups:
You could also store the specific strings in a profile and read the strings in the file.
And the sample will help you to understand it.
AccountController.cs :
IConfidentialClientApplication daemonClient;
daemonClient = ConfidentialClientApplicationBuilder.Create(Startup.clientId)
.WithAuthority(string.Format(AuthorityFormat, tenantId))
.WithRedirectUri(Startup.redirectUri)
.WithClientSecret(Startup.clientSecret)
.Build();
Web.config :
<add key="ida:ClientId" value="[Enter your client ID]" />
<add key="ida:ClientSecret" value="[Enter your client secret]" />
Startup.Auth.cs :
public static string clientId = ConfigurationManager.AppSettings["ida:ClientId"];
public static string clientSecret = ConfigurationManager.AppSettings["ida:ClientSecret"];
public static string redirectUri = "https://localhost:44316/";
did you register an app registration in azure ad, create a secret, set the proper api permissions?
there are even quickstarts in the app registration portal that helps you configure the code and give you a pre set up project that you can experiment with.
Also your code seems to be missing scopes, you need to request the appropriate scopes for graph api token to access groups.
I think the documentation here is better: https://github.com/microsoftgraph/msgraph-sdk-dotnet-auth
and if you check the unit tests here for authorization code flow: https://github.com/microsoftgraph/msgraph-sdk-dotnet-auth/blob/dev/tests/Microsoft.Graph.Auth.Test/ConfidentialClient/AuthorizationCodeProviderTests.cs
gives you a good example of how to make it work.

Basic Auth for SOAP Client Proxy within WebSphere Liberty

We are trying to deploy an EAR on WebSphere Liberty which has been running on WebSphere Application Server 7 before. The application calls an external SOAP Service. The WSDL of the service defines a wsp:Policy with http:BasicAuthentication xmlns:http="http://schemas.microsoft.com/ws/06/2004/policy/http"/
After deployment when we send a request to our application, which would trigger that SOAP-call we get an error:
None of the policy alternatives can be satisfied.
In addition, we get this Warning:
[WARNING ] No assertion builder for type {http://schemas.microsoft.com/ws/06/2004/policy/http}BasicAuthentication registered.
The server.xml file has this feature added:
<feature>wsSecurity-1.1</feature>
The Service Fetching
public IServiceFacade getBasicHttpBindingIServiceFacade() {
return super.getPort(new QName("http://tempuri.org/", "BasicHttpBinding_IService"), IServiceFacade.class);
}
We have previously on WAS 7 been setting the Basic Auth as follows:
IServiceFacade proxy = service.getBasicHttpBindingIServiceFacade();
Map<String, Object> requestContext = ((BindingProvider) proxy).getRequestContext();
((BindingProvider)proxy).getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, endpoint);
/* Basic authentication */
requestContext.put(BindingProvider.USERNAME_PROPERTY, user);
requestContext.put(BindingProvider.PASSWORD_PROPERTY, password);
The following code has been functional on WAS 7 but is failing on Liberty.
UPDATE 1
The issue here seems to be that we are not able to access the cxf ClientProxy from the internal liberty-provided cxf client dependency. After some digging I found that liberty does not expose these libraries. The only solution being, that I need to exclude the jaxws-2.2 and provide all needed dependencies by myself, but as a result of that, I lose all built in functionality provided by liberty with regards to jax-ws's.
https://developer.ibm.com/answers/questions/236182/how-can-i-access-the-libertys-jaxrs-20-apache-cxf/
UPDATE 2
After providing my own cxf jars and excluding the jaxws-2.2 feature from Liberty. I can now access the HTTPConduit through usiing ClientProxy(proxy).getConduit(). However, now the issue seems to be that CXF does not support the provider: http://schemas.microsoft.com/ws/06/2004/policy/http.
It throws the following error:
DEBUG org.apache.cxf.ws.policy.PolicyEngineImpl - Alternative {http://schemas.microsoft.com/ws/06/2004/policy/http}BasicAuthentication is not supported
I have added the following deps to by pom:
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-rs-client</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-ws-security</artifactId>
<version>3.2.0</version>
</dependency>
I also tried adding the following, with no luck:
Endpoint endpoint = client.getEndpoint()
endpoint.getOutInterceptors().add(new WSS4JOutInterceptor())
UPDATE 3
After some help from IBM support I was instructed to follow the following link:
https://www.ibm.com/support/knowledgecenter/SSAW57_liberty/com.ibm.websphere.wlp.nd.multiplatform.doc/ae/twlp_sec_ws_basicauth.html
We added an ibm-ws-bnd.xml file to our META-INF folder (as per section 4 and below), in addition, we used #WebServiceRef to access the webservice defined in our tags in the xml file. The file looks as such:
<?xml version="1.0" encoding="UTF-8"?>
<webservices-bnd xmlns="http://websphere.ibm.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://websphere.ibm.com/xml/ns/javaee http://websphere.ibm.com/xml/ns/javaee/ibm-ws-bnd_1_0.xsd"
version="1.0">
<service-ref name="service/servicename">
<port name="BasicHttpBinding"
namespace="http://ibm.com/ws/jaxws/transport/security/"
username="username"
password="suchwowsecretpassword">
</port>
</service-ref>
</webservices-bnd>
Usign #WebServiceRef, I am getting back the service which is instantiated by the ibm-ws-bnd.xml file. However, the Basic Auth WS-policy is still not satisfied. Upon removing that policy assertion, we can see that the external service is failing with a 401-unauthorized error.
In addition, When we inspect the message in our handlerchain, we can see the following:
We can see that both username and pw values are null on the conduit properties. Which (as per my knowledge), should indicate that ibm-ws-bnd is not setting the actual Basic Auth header on our service.
We basically ran into the same problem a while back [1], but unfortunately were not able to solve this.
My suggestion would be to setup the entire SOAP-Client stuff in normal Java-code and not rely on anything from your Application Server, because then you are able to set the Authentication like the following snippet:
HTTPConduit http = (HTTPConduit) client.getConduit();
http.getAuthorization().setUserName("user");
http.getAuthorization().setPassword("pass");
Note: We actually did not solve our problem like that; We went for a workaround. Our usage of WebSphere Liberty was limited to the Developers-environment. On our Integration Test, Acceptance and Production-server, we use a 'real' WebSphere Application Server.
Our workaround was start to remove the policy line from the WSDL and
not use Basic Authentication in our developers test.
The real WebSphere still applies the HTTP Basic Authentication if it is configured to do so, even if the WSDL does not specify the policy anymore.
I hope you will manage to find a appropriate solution.
Cheers,
Marco
1: How to setup HTTP Basic Authentication for SOAP Client within WebSphere Liberty
Removing accidental edit

How can I use Qt Network Authorization for Azure AD OAuth2

I'm trying to adapt the Qt Network Authorization OAuth2 example for Reddit to work with Azure AD.
I went to https://portal.azure.com/ -> Azure Active Directory -> App registrations then clicked "New application registration" and entered:
Name: QtNetworkAuthProject
Application type: Name
Redirect URI: http://localhost:1337/
I copied the resulting Application ID into the app then got the URIs from Authorization Code Grant Flow:
Authorization Code Request: https://login.microsoftonline.com/common/oauth2/authorize
Access Token Request: https://login.microsoftonline.com/common/oauth2/token
The first part appears to work; the webpage opens and asks me to authenticate the login. But then the token request seems to fail. My logging shows:
AzureWrapper::grant()+
setModifyParametersFunction(): stage = RequestingAuthorization
AzureWrapper::grant()-
statusChanged(): status = TemporaryCredentialsReceived
setModifyParametersFunction(): stage = RequestingAccessToken
qt.networkauth.oauth2: Unexpected call
qt.networkauth.replyhandler: Error transferring https://login.microsoftonline.com/common/oauth2/token - server replied: Bad Request
What have I done wrong?
Azure AD needs in either the authorization code request or in the access token request the App ID URI of the target web API (secured resource) that you want to use. (See https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-protocols-oauth-code)
You can add this extra resource parameter in the authorization code request like this:
oauth2.setModifyParametersFunction([](QAbstractOAuth::Stage stage, QVariantMap* parameters) {
if (stage == QAbstractOAuth::Stage::RequestingAuthorization) {
parameters->insert("resource", "<App ID URI>");
}
});

Resources