List azure ad using msgraph-sdk-java - java

I am trying to bring current user from azure active directory as follows:
final ClientSecretCredential clientSecretCredential = new ClientSecretCredentialBuilder()
.clientId("client_id")
.clientSecret("secret")
.tenantId("tenat_id")
.build();
final TokenCredentialAuthProvider tokenCredentialAuthProvider = new TokenCredentialAuthProvider(Arrays.asList("https://graph.microsoft.com/User.Read.All"), clientSecretCredential);
final GraphServiceClient graphClient =
GraphServiceClient
.builder()
.authenticationProvider(tokenCredentialAuthProvider)
.buildClient();
final User me = graphClient.me().buildRequest().get();
and I am getting the following error:
java.io.IOException: java.util.concurrent.ExecutionException:
com.microsoft.aad.msal4j.MsalServiceException: AADSTS70011: The
provided request must include a 'scope' input parameter. The provided
value for the input parameter 'scope' is not valid. The scope openid
profile offline_access https://graph.microsoft.com/User.Read.All is
not valid. Trace ID: b035aaec-9c8a-4728-a237-6d63738adb00 Correlation
ID: de389e03-9d91-4f4b-aacf-449aa8fac460 Timestamp: 2021-04-05
21:44:43Z
any idea that I may be missing?????

You are using the client credential flow here.
The value passed for the scope parameter in this request should be the
resource identifier (application ID URI) of the resource you want,
affixed with the .default suffix.
The scope should be https://graph.microsoft.com/.default in your issue.

Related

Scope Issue in Azure Graph Rest API Java

I am new using Azure Graph Rest API Java using this repo.
My aim is to list all of the users in the AAD tenant
So far I was only able to get to this:
List<String> scopes= Arrays.asList("https://graph.microsoft.com/User.Read.All");
AzureProfile profile = new AzureProfile(tenantId, subscriptionId, AzureEnvironment.AZURE);
final ClientSecretCredential credential = new ClientSecretCredentialBuilder()
.clientId(clientId)
.clientSecret(clientSecret)
.tenantId(tenantId)
//.httpClient(client)
.authorityHost(profile.getEnvironment().getActiveDirectoryEndpoint())
.build();
TokenCredentialAuthProvider tokenCredentialAuthProvider = new TokenCredentialAuthProvider(scopes, credential);
GraphServiceClient<Request> graphClient =
GraphServiceClient
.builder()
.authenticationProvider(tokenCredentialAuthProvider)
.buildClient();
UserCollectionPage users = graphClient.users()
.buildRequest()
.get();
for(User user: users.getCurrentPage()){
System.out.println(user.displayName);
System.out.println(user.id);
System.out.println(user.userPrincipalName);
}
However, I run into this error instead:
Caused by: java.io.IOException:
java.util.concurrent.ExecutionException:
com.microsoft.aad.msal4j.MsalServiceException:
AADSTS1002012: The
provided value for scope https://graph.microsoft.com/User.Read.All
openid profile offline_access is not valid. Client credential flows
must have a scope value with /.default suffixed to the resource
identifier (application ID URI).
It seems the Scope that I have used is wrong/insufficient, but I am not too sure what should I use the scope with. Any idea?
It is written in the documentation that:
Client credentials requests in your client service must include
scope={resource}/.default. Here, {resource} is the web API that your
app intends to call, and wishes to obtain an access token for. Issuing
a client credentials request by using individual application
permissions (roles) is not supported. All the app roles (application
permissions) that have been granted for that web API are included in
the returned access token.
The Client Credential flow is best suited for situations where you have a Deamon App that will have to authenticate and get access to some kind of a resource through a Non-Interactive way, which in sequence means that the permissions for this Deamon App have been configured and consented from a step done prior to the auth request.
The /.default scope can be translated as the request of the Background App that runs unattended, to get the bulk of the permissions that it has been configured with and access the resource that it asks.
In plain english, the use of the above scope in the Client Credentials flow is a convention that has to be implemented always when this flow is chosen :P.
I tried to reproduce the same in my environment via Postman and got below results:
I registered one Azure AD application and added API permissions like below:
When I tried to generate access token with same scope as you via Postman using client credentials flow, I got same error as below:
POST https://login.microsoftonline.com/<tenantID>/oauth2/v2.0/token
grant_type:client_credentials
client_id: <appID>
client_secret: <secret_value>
scope: https://graph.microsoft.com/User.Read.All openid profile offline_access
Response:
To resolve the above error, you must change your scope to https://graph.microsoft.com/.default if you are using client credentials flow.
After changing the scope, I'm able to generate access token successfully like below:
POST https://login.microsoftonline.com/<tenantID>/oauth2/v2.0/token
grant_type:client_credentials
client_id: <appID>
client_secret: <secret_value>
scope: https://graph.microsoft.com/.default
Response:
When I used the above token to call below Graph query, I got the list of users with display name, id and user principal name successfully like below:
GET https://graph.microsoft.com/v1.0/users?$select=displayName,id,userPrincipalName
Response:
In your case, change scope value in your code like below:
List<String> scopes= Arrays.asList("https://graph.microsoft.com/.default");

SocketTimeOutException for PROXY call using GraphServiceClient, how to configure proxy

I am trying to get list of users from Azure B2C Active Directory, for un-proxy environment, my code is running fine, but when I am running it by passing proxy configuration, I am getting "SocketTimeoutException"
Below is my code...
GraphServiceClient<Request> graphClient;
if (this.proxy.equals("true")) {
final ClientSecretCredential clientSecretCredential = new ClientSecretCredentialBuilder()
.clientId(this.clientId)
.clientSecret(this.clientSecret)
.tenantId(this.b2cTenant)
.build();
final TokenCredentialAuthProvider tokenCredentialAuthProvider =
new TokenCredentialAuthProvider(Constant.scopes, clientSecretCredential);
final Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(this.hostAddress, this.hostPort));
final OkHttpClient httpClient = HttpClients.createDefault(tokenCredentialAuthProvider)
.newBuilder()
.proxy(proxy)
.build();
graphClient = GraphServiceClient.builder()
.authenticationProvider(tokenCredentialAuthProvider)
.httpClient(httpClient)
.buildClient();
} else {
final ClientSecretCredential clientSecretCredential = new ClientSecretCredentialBuilder()
.clientId(this.clientId)
.clientSecret(this.clientSecret)
.tenantId(this.b2cTenant)
.build();
final TokenCredentialAuthProvider tokenCredentialAuthProvider =
new TokenCredentialAuthProvider(Constant.scopes, clientSecretCredential);
graphClient = GraphServiceClient.builder()
.authenticationProvider(tokenCredentialAuthProvider)
.buildClient();
}
In "if" I am working with PROXY and in "else" I am working without PROXY.
So I have a hostAddress and hostPort which I am passing through command line...I am creating a ClientSecretCredential using clientId, clientSecret and b2cTenantId.
Then I am creating a TokenCredentialAuthProvider using scope and client secret credential.
For me scope is - https://graph.microsoft.com/.default
Then I am creating a Proxy using address and port, which I am passing to OkHttpClient. Then I am creating passing all of it to graphClient.
For non-proxy ("else") is working fine, but when I am working through proxy I am getting "Time out". I tried to debug code, but the only exception I could get on calling an API say..
final User me = graphClient.me().buildRequest().get();
I am getting "SocketTimeoutException"
I have read multiple documents, github threads, I am not able to understand the problem.
https://github.com/microsoftgraph/msgraph-sdk-java/issues/162
https://github.com/microsoftgraph/msgraph-sdk-java/issues/158
https://github.com/microsoftgraph/msgraph-sdk-java-core
Please help.
Your code is wrong, the /me endpoint does not support client credential flow. Modify your code and the problem should be resolved.
If you want to list users in the entire tenant, then you need to use the /users endpoint:
final Users users = graphClient.users().buildRequest().get();
If you only need to list a certain user information of tenant, then you can use the /users/{user id} endpoint:
final User user = graphClient.users("{user id}").buildRequest().get();
You need to configure proxy for Azure Identity Client too while using client credential builder as shown below
https://github.com/microsoftgraph/microsoft-graph-docs/pull/12779/commits/89729042a122ecaf4c083ccc18a9e7d565bbec25

OAuth MSAL4J Office365 -> AccessToken error "The token contains no permissions, or permissions can not be understood."

I am currently trying to use the MSAL4J library to get an AccessToken for a ConfidentialClientApplication. I already get the token, but when I pass it to Exchange I get the error "The token contains no permissions, or permissions can not be understood" . I inspected the token with jwt.io and I can see that there is no "scp" field which means there probably are no scopes but I don't know why.
The Java Code for accessing the token:
String AUTHORITY = "https://login.microsoftonline.com/common";
IClientSecret createFromSecret = ClientCredentialFactory.createFromSecret(clientSecret);
Builder builder = ConfidentialClientApplication.builder(clientId, createFromSecret).authority(aUTHORITY).validateAuthority(true);
ConfidentialClientApplication application = builder.build();
HashSet<String> scopes = new HashSet<String>();
scopes.add("https://outlook.office.com/.default");
ClientCredentialParameters clientCredentialParameters = ClientCredentialParameters.builder(scopes).build();
CompletableFuture<IAuthenticationResult> acquireTokenFuture = application.acquireToken(clientCredentialParameters);
IAuthenticationResult iAuthenticationResult = acquireTokenFuture.get(30, TimeUnit.SECONDS);
iAuthenticationResult = acquireTokenFuture.get(30, TimeUnit.SECONDS);
I have added the necessary api permissions and approved them with the admin account:
any ideas whats wrong here?
Seems like I found the answer by myself. In my case I had to add the permissions for exchange, the microsoft graph (as it is used in every example I found) does not work in this scenario or is not sufficient.

google oauth fails fetching redirect url

I'm using Google OAuth2 client-side to authorize a web-app (Liferay Portlet) to use the Calendar Service.
On my Development Server, the whole flow completes successfully:
I start creating a GoogleAuthorizationCodeRequestUrl
Using com.google.api.client.extensions.jetty.auth.oauth2.LocalServerReceiver I create a new redirect URI and set it to wait for Google Response.
a new window/tab opens in user's browser, to googleAuthorizationCodeRequestUrl
user Logs in (if not already logged)
User authorizes the requested scopes
the tab closes automatically, the jetty's Callback URI is fetched from Google,
The flow continues with token exchange etc
But when Deploying on some other remote server (identical environment) the flow gets stuck in step 6. Google seems to be unable to find the redirect_uri. My browsers are landing on an error page, informing that they couldn't establish a connection to the server at localhost:[random port generated from jetty]
Checking the logs, I can see that in both cases (dev/remote server), the redirect_uri created by jetty is localhost:[5digits]/Callback. (not affected by the dns or ip on the remote server) Is this normal ? Did I miss something on the configuration? Maybe jetty was supposed to create another redirect URI, that I should additionally add from Google Dev Console (obviously I cant set to localhost..)
Is it possible that a firewall or proxy setting is blocking the redirect_url?
Any other ideas what I did wrong?
EDIT: posting some code for the URLs creation
GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(httpTransport, jsonFactory, secrets, scopes)
.setDataStoreFactory(dataStore).build();
Credential credents = flow.loadCredential(usermail);
String redirect_url = null;
// Checking if the given user is not authorized
if (credents == null) {
// Creating a local receiver
LocalServerReceiver receiver = new LocalServerReceiver();
try {
// Getting the redirect URI
String redirectUri = receiver.getRedirectUri();
// Creating a new authorization URL
AuthorizationCodeRequestUrl authorizationUrl = flow.newAuthorizationUrl();
// Setting the redirect URI
authorizationUrl.setRedirectUri(redirectUri);
// Building the authorization URL
String url = authorizationUrl.build();
// Logging a short message
log.info("Creating the authorization URL : " + url);
//This url will be fetched right after, as a button callback (target:_blank)
//by using :FacesContext.getCurrentInstance().getExternalContext().redirect(googleAuthUrl);
googleAuthUrl = url;
// Receiving authorization code
String code = receiver.waitForCode();
// Exchanging it for an access token
TokenResponse response = flow.newTokenRequest(code).setRedirectUri(redirectUri).execute();
// Storing the credentials for later access
credents = flow.createAndStoreCredential(response, id);
} finally {
// Releasing resources
receiver.stop();
}
}
// Setting up the calendar service client
client = new com.google.api.services.calendar.Calendar.Builder(httpTransport, jsonFactory, credents).setApplicationName(APPLICATION_NAME)
.build();
Instead of creating an instance of LocalServerReceiver by this:
// Creating a local receiver
LocalServerReceiver receiver = new LocalServerReceiver();
You should do this by using a LocalServerReceiver.Builder.
According to documentation non-parameter constructor is: Constructor that starts the server on "localhost" selects an unused port.
So you can use builder, set proper host name (remote server) and build LocalServerReceiver instance. (or you can use LocalServerReceiver(host, port) constructor)
This should set redirect_uri to proper address.

Requested scopes not allowed

I'm trying to fetch a user from Google Directory API with the following request:
Collection<String> SCOPES = Arrays.asList("https://www.googleapis.com/auth/admin.directory.user.readonly", "https://www.googleapis.com/auth/admin.directory.user");
URL url = getClass().getResource("privatekey.p12");
GoogleCredential credential = new GoogleCredential.Builder()
.setJsonFactory(request.getJsonFactory())
.setServiceAccountId("foobar.com")
.setServiceAccountScopes(SCOPES)
.setTransport(new NetHttpTransport())
.setServiceAccountUser("foo#bar.com")
.setServiceAccountPrivateKeyFromP12File(new File(url.getPath())).build();
Directory dir = new Directory.Builder(GoogleNetHttpTransport.newTrustedTransport(), request.getJsonFactory(), credential)
.setApplicationName(request.getApplicationName())
.build();
But I'm getting the following error from the API:
Caused by: com.google.api.client.auth.oauth2.TokenResponseException: 400 Bad Request
{
"error" : "access_denied",
"error_description" : "Requested scopes not allowed: https://www.googleapis.com/auth/admin.directory.user https://www.googleapis.com/auth/admin.directory.user.readonly"
}
I've checked the scopes are correct, anyone know what's the problem here?
Try using one scope or the other. If you only need read access, use the readonly scope. If you need read/write access to users, use the other.
Also, ensure that you've granted the service account's client_id access to these scopes in the Admin's control panel (admin.google.com) and that your ServiceAccountUser is a super admin in the domain.
Argh! I removed the old service account and created a new one. After that things started to roll. Guessing I had a wrong p12 file etc. Can finally breathe again, phew.

Categories