I would like to retrieve all devices managed by Intune (managed devices) using the Microsoft Graph Java SDK. I have created the app in Microsoft Azure and given the appropriate API permissions:
API Permissions
The following code creates a graphClient object and a method that retrieves all managed devices.
#Service
public class AzureServiceDefault implements AzureService
{
private static final String CLIENT_ID = "XXXXXXXXXXXXXXXXXXXXXXXX";
private static final List<String> SCOPES = Arrays.asList(new String[]{"https://graph.microsoft.com/.default"});
private static final String TENANT = "XXXXXXXXXXXXXXXXXXXXXXXX";
private static final String CLIENT_SECRET = "XXXXXXXXXXXXXXXXXXXXXXXX";
ClientCredentialProvider authProvider = new ClientCredentialProvider(CLIENT_ID, SCOPES, CLIENT_SECRET, TENANT, NationalCloud.Global);
IGraphServiceClient graphClient;
public AzureServiceDefault()
{
graphClient = GraphServiceClient.builder().authenticationProvider(authProvider).buildClient();
}
#Override
public List<IntuneDevice> getManagedDevices()
{
IManagedDeviceCollectionRequestBuilder managedDeviceRequestBuilder;
IDeviceManagementRequestBuilder builder = graphClient.deviceManagement();
IDeviceManagementRequest managedDevicesRequest = builder.buildRequest();
List<ManagedDevice> managedDevices = new ArrayList<>();
List<IntuneDevice> allManagedDevices = new ArrayList<>();
do {
try {
DeviceManagement deviceManagement = managedDevicesRequest.get();
ManagedDeviceCollectionPage managedDevicesCollectionPage = deviceManagement.managedDevices;
//Process items in the response
managedDevices.addAll(managedDevicesCollectionPage.getCurrentPage());
managedDevices.stream().forEach((device) -> allManagedDevices.add(new IntuneDevice(device.id,
device.userId,
device.deviceName,
device.managedDeviceOwnerType.toString(),
device.operatingSystem,
device.osVersion,
device.complianceState.toString(),
device.azureADRegistered,
device.azureADDeviceId,
device.userPrincipalName,
device.model,
device.manufacturer,
device.serialNumber)));
//Build the request for the next page, if there is one
managedDeviceRequestBuilder = managedDevicesCollectionPage.getNextPage();
if (managedDeviceRequestBuilder == null)
{
managedDevicesRequest = null;
}
else
{
managedDevicesRequest = (IDeviceManagementRequest) managedDeviceRequestBuilder.buildRequest();
}
}
catch(ClientException ex)
{
ex.printStackTrace();
managedDevicesRequest = null;
}
} while (managedDevicesRequest != null);
return allManagedDevices;
}
}
The problem is that the variable managedDevices turns out to be null and this is the error message:
SEVERE: Servlet.service() for servlet [dispatcher] in context with path [/] threw exception [Request processing failed; nested exception is java.lang.NullPointerException: Cannot invoke "com.microsoft.graph.requests.extensions.ManagedDeviceCollectionPage.getCurrentPage()" because "managedDevicesCollectionPage" is null] with root cause
java.lang.NullPointerException: Cannot invoke "com.microsoft.graph.requests.extensions.ManagedDeviceCollectionPage.getCurrentPage()" because "managedDevicesCollectionPage" is null
What do I need to change to make this code work? I am succesfully able to retrieve all users in Azure AD, but I am having difficulties getting data from Intune/Endpoint Manager. Do I need to make changes to the SCOPES?
It should be possible to retrieve all managed devices as the REST API for it is https://graph.microsoft.com/v1.0/deviceManagement/managedDevices
Thanks for your help
This MS Graph API does not support application permissions, so you couldn't list managedDevices with ClientCredentialProvider. ClientCredentialProvider is based on client credential flow that requires application permission.
You could use AuthorizationCodeProvider to get the list. And follow this to get AUTHORIZATION_CODE first.
String CLIENT_ID = "xxxxxx";
List<String> SCOPES = Arrays.asList(new String[] { "https://graph.microsoft.com/.default" });
String CLIENT_SECRET = "xxxxxx";
String TENANT = "xxxxxx";
String AUTHORIZATION_CODE = "";
String REDIRECT_URL = "xxxxxx";
AuthorizationCodeProvider authProvider = new AuthorizationCodeProvider(CLIENT_ID, SCOPES, AUTHORIZATION_CODE,
REDIRECT_URL, NationalCloud.Global, TENANT, CLIENT_SECRET);
IGraphServiceClient graphClient = GraphServiceClient.builder().authenticationProvider(authProvider).buildClient();
IManagedDeviceCollectionPage managedDeviceCollectionPage = graphClient.deviceManagement().managedDevices().buildRequest().get();
List<ManagedDevice> managedDeviceList = managedDeviceCollectionPage.getCurrentPage();
Related
I need to connect to SharePoint in order to upload files to https://xxxxxxgroup.sharepoint.com/sites/xxx-xxxxxDepartment from a webapp I developed.
I registered my app and all I have is:
tennantId
clientId
pemCertificate
so I'm using msgraph-sdk-java and as authentication OnBehalfOf Provider. below is my code:
public GraphServiceClient sharepointAuth() {
private final static String CLIENT_ID = "xxxxxxxxx-xxxx-xxxxx-xxxx-xxxxxxxx";
private final static String TENANT_ID = "xxxxxxxxx-xxxx-xxxxx-xxxx-xxxxxxxx";
private final static List<String> SCOPES = Arrays.asList("xxxxx");
private final static String CERTIFICATE_PATH = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
try {
final OnBehalfOfCredential onBehalfOfCredential = new OnBehalfOfCredentialBuilder()
.clientId(CLIENT_ID)
.tenantId(TENANT_ID)
.pemCertificate(CERTIFICATE_PATH)
//.userAssertion()
.build();
final TokenCredentialAuthProvider tokenCredentialAuthProvider = new TokenCredentialAuthProvider(SCOPES, credential1);
final GraphServiceClient graphClient = GraphServiceClient
.builder()
.authenticationProvider(tokenCredentialAuthProvider)
.buildClient();
System.out.println("------after auth------");
final Drive result = graphClient
.me()
.drive()
.buildRequest()
.get();
System.out.println("Found Drive " + result.id);
} catch(Exception e) {
System.out.println("exception");
System.out.println(e);
}
return null;
}
I'm not facing any error but I'm not able to get the drive, in logs:
------after auth------
I didn't find any example online, this is my 3rd full day working on it, I have many question:
pemCertificate: must be absolute/root path? (I tried both and nothing succeded)
userAssertion: is it mandatory? and how to fill it?
any example on how to connect to sharepoint? (if I only have tenantId, clienId and pemCertificate)
I appreciate your help!
I am getting below error when I try to use #Async annotation.
java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
Code:
#Async
#Override
#Transactional
public void triggerEmailCommunication(List<String> eventCode, Integer authorizationId, Integer claimId,
boolean callMethod) {
Map<String, Object> emailBody = new HashMap<>();
try {
if (callMethod) {
LOGGER.info("Communication async flow triggerEmailCommunication method starts.");
emailBody = emailBodyObject(authorizationId, claimId, eventCode);
}
private Map<String, Object> emailBodyObject(Integer authorizationId, Integer claimId, List<String> eventCode)
throws CareBusinessServiceException {
LOGGER.info("EmailBodyObject method starts.");
Map<String, Object> emailBody = new HashMap<String, Object>();
EmailClaimDetailsVO emailClaimDetails = new EmailClaimDetailsVO();
ClaimAuthorizationVO claimAuthVO = new ClaimAuthorizationVO();
Claim claim = new Claim();
Authorization authorization = new Authorization();
List<String> rejectReasonList = new ArrayList<>();
Provider provider = new Provider();
String providerName = null;
String claimIntimationNbr = null;
String authorizationNbr = null;
SimpleDateFormat formatter = new SimpleDateFormat(AmhiConstants.DATE_FORMAT_DD_MM_YYYY);
try {
Integer claimIntimationId = null;
if (null != claimId) {
claim = enableBusinessService.retrieveClaimDetails(claimId);
} catch(Exception e) {
}
}
DAO Layer
#Override
public Claim retrieveClaimIdRecord(Integer claimId) {
CriteriaBuilder builder = manager.getCriteriaBuilder();
CriteriaQuery<Claim> criteriaQuery = builder.createQuery(Claim.class);
Root<Claim> root = criteriaQuery.from(Claim.class);
ArrayList<Predicate> conditions = new ArrayList<>();
conditions.add(builder.equal(root.get(Claim_.claimId), claimId));
criteriaQuery.select(root).where(conditions.toArray(new Predicate[] {}));
javax.persistence.Query query = manager.createQuery(criteriaQuery);
List<Claim> claims = query.getResultList();
if(CollectionUtils.isNotEmpty(claims)){
return claims.get(0);
}
return new Claim();
}
The value is getting retrieved from DB. But I am getting above exception as mentioned.
In my case it happened when I created a bean at request level and tried to access it from another thread created using #Async annotation. The bean information was stored in the thread local of the original thread only.
Are you using something created and stored in the thread local of the base thread in the function or subsequent functions after #Async notification?
#Async creates another thread but doesn't copy the thread local of the original thread which causes the issue.
See this - https://jira.spring.io/browse/SPR-6873
I'm building an application that needs access to our clients' Office 365 Management Activities. I've followed the steps outlined in this Azure Active Directory overview, and am able to use the OAuth code to acquire an initial Access Token, as well as use this token to set up O365 subscriptions.
However, when I use the refresh_token provided with my initial token to acquire a new Access Token, I get the following error:
{"error_description":"AADSTS65001: The user or administrator has not consented to use the application with ID '8f72f805-dfd2-428d-8b0e-771a98d26c16'. Send an interactive authorization request for this user and resource.\r\nTrace ID: df229c3f-8f28-420b-9ac3-321ab1b2ad09\r\nCorrelation ID: 0e0f2bcb-4b19-458a-8556-2a6d4e51379f\r\nTimestamp: 2016-10-03 17:33:20Z","error":"invalid_grant"}
Since I'm able to acquire and use the initial Access Token, I'm pretty sure that the user is granting my applications some permissions. Is there a specific permission that I need in order to acquire a new Access Token using the Refresh Token?
Edit:
Specifically, I'm using the com.microsoft.azure::adal4j java package, AuthenticationContext class, acquireTokenByAuthorizationCode and acquireTokenByRefreshToken methods:
public class AzureProvisioner {
private final AuthenticationContext authService = new AuthenticationContext(
"https://login.windows.net/common/oauth2/token", true, Executors.newSingleThreadExecutor());
private final ClientCredential clientCredential = new ClientCredential("azureAppId", "azureAppSecret");
public static final String resource = "https://manage.office.com";
// Internal implementation of REST interface; Microsoft didn't provide a Java Library
final Office365ManagementApi managementApi;
public void acquireToken(final String authCode, final URI redirectUri) {
final AuthenticationResult authResult = authService.acquireTokenByAuthorizationCode(
authCode, redirectUri, clientCredential, resource, null).get()
// internal library code, gets the "tid" field from parsing the JWT token
final String tenantId = JwtAccessToken.fromToken(authResult.getAccessToken()).getTid();
// works
createInitialSubscription(customerId, authResult.getAccessToken(), tenantId);
// throws an error
final AuthenticationResult refreshResult = authService.acquireTokenByRefreshToken(
authResult.getRefreshToken(), clientCredential, null).get();
}
private void createInitialSubscription(final String accessToken, final String tenantId) {
final String authHeader = "Authorization: Bearer " + accessToken;
final String contentType = "Audit.AzureActiveDirectory";
// internal implementation
final CreateWebhookRequest requestBody = new CreateWebhookRequest();
managementApi.createSubscription(authHeader, tenantId, contentType, requestBody);
}
}
The same code, without any external dependencies, also does not work for me:
public class AzureProvisioner {
private final AuthenticationContext authService = new AuthenticationContext(
"https://login.windows.net/common/oauth2/token", true, Executors.newSingleThreadExecutor());
private final ClientCredential clientCredential = new ClientCredential("8f72f805-dfd2-428d-8b0e-771a98d26c16", "secret");
public final String resource = "https://manage.office.com";
private URI redirectUri = new URI("https://localhost");
private static final String oAuthUrl = "https://login.windows.net/common/oauth2/authorize?response_type=code&client_id=8f72f805-dfd2-428d-8b0e-771a98d26c16&resource=https%3A%2F%2Fmanage.office.com&redirect_uri=https%3A%2F%2Flocalhost";
public AzureProvisioner() throws Exception {
// do nothing
}
public static void main(String... args) throws Exception {
final String authCode = "AQABAAAAAADRNYRQ3dhRSrm...";
new AzureProvisioner().acquireToken(authCode);
}
public void acquireToken(final String authCode) throws Exception {
final AuthenticationResult authResult = authService.acquireTokenByAuthorizationCode(
authCode, redirectUri, clientCredential, resource, null).get();
System.out.println(authResult.getAccessToken());
// throws an error
final AuthenticationResult refreshResult = authService.acquireTokenByRefreshToken(
authResult.getRefreshToken(), clientCredential, resource, null).get();
System.out.println(refreshResult.getAccessToken());
}
}
Using a proxy, I took a trace of the https refresh request:
Method: POST
Protocol-Version: HTTP/1.1
Protocol: https
Host: login.windows.net
File: /common/oauth2/token
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Connection: keep-alive
Content-Length: 876
refresh_token={token}
&resource=https%3A%2F%2Fmanage.office.com
&grant_type=refresh_token
&scope=openid
&client_secret={secret}
&client_id=8f72f805-dfd2-428d-8b0e-771a98d26c16
It turns out that the root issue was with my application permissions. Under My Application > Settings > Required Permissions > Office 365 Management APIs, I had selected the "Application Permissions", where I needed to select the "Delegated Permissions". Swapping those over, my code immediately started working as expected.
ADAL uses the stored refresh tokens automatically and transparently, you aren't required to perform any explicit action. AcquireTOkenByRefreshToken is in the ADAL surface for legacy reasons, and has been removed from version 3.x. More background at http://www.cloudidentity.com/blog/2015/08/13/adal-3-didnt-return-refresh-tokens-for-5-months-and-nobody-noticed/
I'm trying to obtain a list of a user's tweets and I've run into some trouble when trying to authenticate my call to the API. I currently get a 401 when executing the code below:
public interface TwitterApi {
String API_URL = "https://api.twitter.com/1.1";
String CONSUMER_KEY = "<CONSUMER KEY GOES HERE>";
String CONSUMER_SECRET = "<CONSUMER SECRET GOES HERE>";
String ACCESS_TOKEN = "<ACCESS TOKEN GOES HERE>";
String ACCESS_TOKEN_SECRET = "<ACCESS TOKEN SECRET GOES HERE>";
#GET("/statuses/user_timeline.json")
List<Tweet> fetchUserTimeline(
#Query("count") final int count,
#Query("screen_name") final String screenName);
}
The following throws a 401 Authorisation error when calling fetchUserTimeline()
RetrofitHttpOAuthConsumer consumer = new RetrofitHttpOAuthConsumer(TwitterApi.CONSUMER_KEY, TwitterApi.CONSUMER_SECRET);
consumer.setTokenWithSecret(TwitterApi.ACCESS_TOKEN, TwitterApi.ACCESS_TOKEN_SECRET);
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(TwitterApi.API_URL)
.setClient(new SigningOkClient(consumer))
.build();
TwitterApi twitterApi = restAdapter.create(TwitterApi.class)
tweets = twitterApi.fetchUserTimeline(2, screenName);
I've also included the relevant code from the signpost-retrofit plugin:
public class SigningOkClient extends OkClient {
private final RetrofitHttpOAuthConsumer mOAuthConsumer;
public SigningOkClient(RetrofitHttpOAuthConsumer consumer) {
mOAuthConsumer = consumer;
}
public SigningOkClient(OkHttpClient client, RetrofitHttpOAuthConsumer consumer) {
super(client);
mOAuthConsumer = consumer;
}
#Override
public Response execute(Request request) throws IOException {
Request requestToSend = request;
try {
HttpRequestAdapter signedAdapter = (HttpRequestAdapter) mOAuthConsumer.sign(request);
requestToSend = (Request) signedAdapter.unwrap();
} catch (OAuthMessageSignerException | OAuthExpectationFailedException | OAuthCommunicationException e) {
// Fail to sign, ignore
e.printStackTrace();
}
return super.execute(requestToSend);
}
}
The signpost-retrofit plugin can be found here: https://github.com/pakerfeldt/signpost-retrofit
public class RetrofitHttpOAuthConsumer extends AbstractOAuthConsumer {
private static final long serialVersionUID = 1L;
public RetrofitHttpOAuthConsumer(String consumerKey, String consumerSecret) {
super(consumerKey, consumerSecret);
}
#Override
protected HttpRequest wrap(Object request) {
if (!(request instanceof retrofit.client.Request)) {
throw new IllegalArgumentException("This consumer expects requests of type " + retrofit.client.Request.class.getCanonicalName());
}
return new HttpRequestAdapter((Request) request);
}
}
Any help here would be great. The solution doesn't have to include the use of signpost but I do want to use Retrofit. I also do not want to show the user an 'Authenticate with Twitter' screen in a WebView - I simply want to display a handful of relevant tweets as part of a detail view.
Are you certain the signpost-retrofit project works for twitter oauth? I've used twitter4j successfully in the past - and if you don't want the full library you can use their code for reference. twitter4j
I am trying to implement a Facebook Connection using Spring Social, based on this example(from spring social manual):
FacebookConnectionFactory connectionFactory =
new FacebookConnectionFactory("clientId", "clientSecret");
OAuth2Operations oauthOperations = connectionFactory.getOAuthOperations();
OAuth2Parameters params = new OAuth2Parameters();
params.setRedirectUri("https://my-callback-url");
String authorizeUrl = oauthOperations.buildAuthorizeUrl(GrantType.AUTHORIZATION_CODE, params);
response.sendRedirect(authorizeUrl);
// upon receiving the callback from the provider:
AccessGrant accessGrant = oauthOperations.exchangeForAccess(authorizationCode, "https://my-callback-url", null);
Connection<Facebook> connection = connectionFactory.createConnection(accessGrant);
My problem is that I don't know what my redirect url should be.My code is this:
#RequestMapping("/face")
public String communicate() {
FacebookConnectionFactory connectionFactory =
new FacebookConnectionFactory(clientId, clientSecret);
OAuth2Operations oauthOperations = connectionFactory.getOAuthOperations();
OAuth2Parameters params = new OAuth2Parameters();
//this remdirectUri should be another one?
params.setRedirectUri("http://dev01.spring:8080/spring/face");
String authorizeUrl = oauthOperations.buildAuthorizeUrl(GrantType.AUTHORIZATION_CODE, params);
System.out.println(authorizeUrl);
//return "redirect:"+authorizeUrl;
// upon receiving the callback from the provider:
//AccessGrant accessGrant = oauthOperations.exchangeForAccess(authorizationCode, "https://my-callback-url", null);
//Connection<Facebook> connection = connectionFactory.createConnection(accessGrant);
}
My authorizeUrl is like this(from System.out line):
https://graph.facebook.com/oauth/authorize?client_id=myId&response_type=code&redirect_uri=http%3A%2F%2Fdev01.spring%3A8080%2Fspring%2Fface
If i uncomment the line where I continue the Oauth flow redirecting to this authorizeUrl, i'm getting the following error: This webpage has a redirect loop.
So my question is, what the redirect uri should be.Thank you.
Very late edit, in the hopes it helps someone. This is my controller and the method that does the whole Oauth2 dance. I must add that this worked when the question was asked, I have no idea how it behaves now.
#Controller
public class FacebookController {
private static final String clientId = "clientIdHere"; // clientId from facebook app
private static final String clientSecret = "clientSecret here"; // clientSecret
private FacebookConnectionFactory connectionFactory; // facebookConnectionFactory
/*
* If an authorization was given by provider(code) we get an token and bind the api.
*/
#RequestMapping("/facebook/callback")
public String authorize(#RequestParam("code") String authorizationCode,Model model) {
// exchange facebook code with an access token.
AccessGrant accessGrant = connectionFactory.getOAuthOperations().exchangeForAccess(authorizationCode, "http://localhost:8080/testApp/facebook/callback", null); // not that the application was deployed at "http://localhost:8080/testApp"
// connect to facebook with the given token.
Connection<Facebook> connection = connectionFactory.createConnection(accessGrant);
// bind the api
Facebook facebook = connection.getApi();
// get user profile informations
FacebookProfile userProfile = facebook.userOperations().getUserProfile();
// At this point you have acces to the facebook api.
// For ex you can get data about the user profile like this
// create user with facebook's user accounts details.
User facebookUser = new User(userProfile.getFirstName(),
userProfile.getLastName(),
userProfile.getEmail(),
Role.ROLE_FACEBOOKUSER,
"socialuser");
return "redirect:/home";
}
}