I am trying to call Directory APIs from my GAE application in JSP. The application is already running on AppSpot. I'd like to retrieve all organizational units that a user belong to. Unfortunately I get 404 code while making the request and I have no idea why.
ArrayList<String> scopes = new ArrayList<String>();
scopes.add("https://www.googleapis.com/auth/admin.directory.user");
AppIdentityService appIdentity = AppIdentityServiceFactory.getAppIdentityService();
AppIdentityService.GetAccessTokenResult accessToken = appIdentity.getAccessToken(scopes);
URL url = new URL("https://www.googleapis.com/admin/directory/v1/users/myuser#mygoogleappsdomain.com");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoOutput(true);
connection.setRequestMethod("POST");
connection.addRequestProperty("Content-Type", "application/json");
connection.addRequestProperty("Authorization", "OAuth " + accessToken.getAccessToken());
if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
out.print("OK");
}
else {
out.print(connection.getResponseCode());
}
As you can imagine this code snippet prints 404. Basically I am following an example that is available on the GAE documentation. What am i doing wrong? Thank you.
EDIT: If I just call one of the following URLs I get a 403 status code. Is there anything wrong with my OAuth authentication?
https://www.googleapis.com/admin/directory/v1/users?domain=mydomain
https://www.googleapis.com/admin/directory/v1/users
Your code only provides app identity. You will also need to get authorisation from user to get access to their directory info.
If you follow the link you provided you get to the point that states: All requests to the Directory API must be authorized by an authenticated user.
So you will need to send your users through a OAuth 2 authentication + authorization procedure, where you will ask them for Directory API access. If you only need a read-only access to list of users then you will need to request a https://www.googleapis.com/auth/admin.directory.user.readonly scope.
Related
I'm trying to use the azure Graph API to fetch the users having a particular role. Code snippet below as how I generated the authToken
AuthenticationContext authenticationContext = new AuthenticationContext(_authString, false);
ClientCredential clientCred = new ClientCredential(_clientId, _clientSecret);
AuthenticationResult authenticationResult;
if (_authenticationResult == null || true == CanAcquireToken)
{
authenticationResult = authenticationContext.AcquireTokenAsync(_resAzureGraphAPI, clientCred).GetAwaiter().GetResult();
_authenticationResult = authenticationResult;
}
return _authenticationResult.AccessToken;
Snapshot of the list of permissions the Graph API has on my application:
GraphAPI permissions on application
I did my reading from https://learn.microsoft.com/en-us/graph/api/rbacapplication-list-roleassignments?view=graph-rest-beta&tabs=http to understand the API endpoint and the required set of permissions for the Graph API on the corresponding appRegistration.
I have provided the graph API with required permission set on the application which is under review.
However, the API request to
https://graph.microsoft.com/{{beta}}/roleManagement/directory/roleAssignments?$filter=roleDefinitionId eq '{{roleDefnId-Guid}}'
always keeps failing with the error details
Access token validation failure. Invalid audience
Note: The generated auth token is successfully fetching me info from "https://learn.microsoft.com/en-us/graph/api/approleassignment-get?view=graph-rest-beta&tabs=http" mentioned calls
The permission of your app is correct, Directory.ReadWrite.All application permission is enough. I also test your code, it works fine.
Make sure the _resAzureGraphAPI in your code is https://graph.microsoft.com (just confirm it again, I know if it is wrong, you will not be able to call the api in your Note). I can just reproduce your issue with a wrong one, e.g. https://graph.windows.net.
If the _resAzureGraphAPI is correct, decode your token in https://jwt.io/, see it is the same as below.
I'm having a little trouble following how API Access delegate permissions work with azure active directory. I feel like i'm probably misunderstanding a key aspect of how AAD works.
Here is my set up
I have a Web Application let’s call it WebApp. I have created
an AAD for the Web Application and registered with a AAD App ID. Let’s
call it App ID A
I have a Web Api let’s call it ApiService. I have also created an AAD for it and registered with a AAD App ID. Let’s all it App ID B.
In AAD App ID A, I have updated the clicked on the API Access ->
Required Permissions -> Add (App ID B ; Web API) permissions
I’ve updated the manaifest in the AAD App ID B, to give consent to
knownClientApplications to include the client ID of the Web App
I’ve also enable oauth2AllowImplicitFlow to be true for both App’s
manifest.
What I’m trying to do is, A user signs into the web application sign. When it signs in, the user is able to acquire a token for the specific Web App App ID A. The user should be able to use that token and have access the Api Service with App ID B. I thought by configuring the whole API Access -> Required Permissions within the Web Application it would give me delegate permission with the logged in user to communicate with the Api Service WebApi.
When I examine the JWT token, I notice that there is a claim for Microsoft Graph, but not for the ApiService. Shouldn’t I be seeing a claim?
When I try to use the token, it reacts with a 404 authentication error.
Any advice appreciated,
Thanks,
Derek
UPDATE
In response to #joonasw
I actually looked at the example you wrote when i started.
https://joonasw.net/view/aspnet-core-2-azure-ad-authentication
In the example, the web application is initialized with:
.AddOpenIdConnect(opts =>
{
Configuration.GetSection("OpenIdConnect").Bind(opts);
opts.Events = new OpenIdConnectEvents
{
OnAuthorizationCodeReceived = ctx =>
{
return Task.CompletedTask;
}
};
});
In the HomeController, there is code to retrieve the token for the graph api
private async Task<string> GetAccessTokenAsync()
{
string authority = _authOptions.Authority;
string userId = User.FindFirstValue("http://schemas.microsoft.com/identity/claims/objectidentifier");
var cache = new AdalDistributedTokenCache(_cache, _dataProtectionProvider, userId);
var authContext = new AuthenticationContext(authority, cache);
//App's credentials may be needed if access tokens need to be refreshed with a refresh token
string clientId = _authOptions.ClientId;
string clientSecret = _authOptions.ClientSecret;
var credential = new ClientCredential(clientId, clientSecret);
var result = await authContext.AcquireTokenSilentAsync(
"https://graph.microsoft.com",
credential,
new UserIdentifier(userId, UserIdentifierType.UniqueId));
return result.AccessToken;
}
From my understanding, when the user initially login to the web application it will trigger the OnAuthorizationCodeReceived() method where it will be using the clientId/clientSecret/resource of the web applicaiton. The token is stored in the distributed token cache under the key resource/client id.
In the example, GetAccessTokenAsync() is used to grab the token to access the graph API.
In my case, I was hoping to update that method to retrieve the token for the WebApi which has a different clientId/clientSecret/resoruce. In my case, it will AcquireTokenSilentAsync will throw an AdalTokenAcquisitionExceptionFilter because the token needed is not stored in the cache and in the AdalTokenAcquisitionExceptionFilter it will call try to reauthenticate
context.Result = new ChallengeResult();
which will redirect to the authentication page and then hits the AddOpenIdConnect() method. However, the openIdConnect is configured with the web app clientID/ClientSecret/Resource and will not store the new token properly. It will try to call GetAccessTokenAsync() again and the whole process will go in an infinite loop.
In the example, if you were to comment out the "Anthentication:resource" in app.settings, you will experience the same issue with the infinite loop. What happens is that you initially authenticate correctly with no resource specified. Then when you click on you try to get the token for microsoft graph which is a new resource, it can't find it in the cache and then tries to reauthenticate over and over again.
I also notice that the acquireAsyncAuthentication only returns a AuthenticationResult with a bearer tokentype. How would you get the refresh token in this case?
Any advice?
Thanks,
Derek
UPDATE (Solution)
Thanks to #jaanus. All you have to do is update the resource to the clientid of the web api and pass that into AcquireTokenSilentAsync. The web api id uri that you can get from the azure portal did not work.
Okay, so it seems there are multiple questions here. I'll try to make some sense of this stuff to you.
Adding the "Web App"'s client id to the "ApiService" knownClientApplications is a good idea.
It allows for consent to be done for both apps at the same time. This really only matters for multi-tenant scenarios though.
Now, your Web App will be acquiring access tokens at some point.
When it does, it must specify a resource parameter.
This parameter says to AAD which API you wish to call.
In the case of the "ApiService", you should use either its client id or Application ID URI (this is more common).
Depending on the type of your Web App, the access token is acquired a bit differently.
For "traditional" back-end apps, the Authorization Code Grant flow is usually used.
In this flow your back-end gets an authorization code after the user logs in, and your Web App can then exchange that code for the access token.
In the case of a front-end JavaScript app, you would use the Implicit Grant flow, which you have allowed (no need to enable it in the API by the way).
This one allows you to get access tokens directly from the authorization endpoint (/oauth2/authorize) without talking to the token endpoint as you usually have to.
You can actually get the access token right away after login in the fragment of the URL if you wish.
ADAL.JS makes this quite a lot easier for you if you are going in this route.
The reason you get the authentication error is because the access token is probably meant for Microsoft Graph API. You need to request an access token for your API.
An access token is always only valid for one API.
I am trying to post a file to a URL programmatically. This URL is protected by its own username/password.
I have inherited this URL (or application) - so it is in my ownership. It is basically a web application used to host some files.
I have created an HttpClient which takes HttpPost consisting of the URL and file to be uploaded.
this.httpClient = new DefaultHttpClient();
this.httpClient.getCredentialsProvider().setCredentials(new AuthScope(this.host, this.port), new UsernamePasswordCredentials(this.username, this.password));
HttpResponse response = this.httpClient.execute(httpPost, this.context);
HttpEntity resEntity = response.getEntity();
log("Received HTTP response: " + response.getStatusLine().getReasonPhrase(), LogLevel.INFO.getLevel());
But somehow weblogic authentication blocks it and I receive "Unauthorized" response. If I use the username/password of weblogic admin console, then authentication is fine but fails while uploading since the URL is protected by a different credential(Same as the one I am trying to upload with).
Any idea what am I doing worng? Why weblogic is asking for its own authentication?
I found the solution for this issue. I needed to set enforce-valid-basic-auth-credentials equal to false in domain config.xml . This bypasses weblogic authentication and the application authentication kicks in.
I have a problem (or two) with regards to accessing my office 365 account via the Microsoft Graph API.
The first issue is that I have a java program that is attempting to list all users in the office 365 subscription. I am calling https://graph.microsoft.com/v1.0/users/ but getting a 403 forbidden back.
On the App registration, I have added permissions including User.Read, User.ReadBasic.All, User.ReadWrite on both delegated and app permissions.
I have also tried to use the Graph Explorer, but when I enter to use my account it still uses the built in graph user and doesn't show my application login info. Not sure if these are related.
Here is code snippet that results in a 403
AuthenticationResult result = getAccessTokenFromUserCredentials(RESOURCE_GRAPH, ID, PASSWORD);
URL url = new URL("https://graph.microsoft.com/v1.0/users/") ;
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("Accept", "application/json");
conn.setRequestProperty("Authorization", "Bearer "+result.getAccessToken());
if (conn.getResponseCode() != 200) {
throw new RuntimeException("Failed : HTTP error code : "
+ conn.getResponseCode());
}
And here is the method that gets the token
private static AuthenticationResult getAccessTokenFromUserCredentials(String resource,
String username, String password) throws Exception {
AuthenticationContext context;
AuthenticationResult result = null;
ExecutorService service = null;
try {
service = Executors.newFixedThreadPool(1);
context = new AuthenticationContext(AUTHORITY, false, service);
Future<AuthenticationResult> future = context.acquireToken(
resource, CLIENT_ID, username, password,
null);
result = future.get();
} finally {
service.shutdown();
}
if (result == null) {
throw new ServiceUnavailableException(
"authentication result was null");
}
return result;
}
The app register in apps.dev.microsoft.com works with the v2.0 endpoint .Please click here for more details about the v2.0 endpoint .
You can acquiring token using v2.0 authentication protocols and Azure Active Directory v2.0 authentication libraries . During authentication , you need to do user consent or admin consent for User.ReadBasic.All permission . After consenting , access token includes that delegate permission and will work when calling list users operation .
OK, thought I should post up the answer. Firstly, and most confusingly, the apps.dev.microsoft.com registration didn't seem to work (even though I was using the V2.0 endpoint and the version 2 libraries).
However, when I registered the app using the azure portal directly, this fixed the issue. I have subsequently been able to access the service correctly.
It seems strange that, although the authentication / authorisation service was standard for my app and worked perfectly for accessing Sharepoint / One Drive etc, but, when wanting to hit the users endpoint, it would only work if it was registered in the portal.azure.com.
Many thanks everyone for your help.
I need to know how to obtain documents from a specific user, the user is authenticated using ...
UserService userService = UserServiceFactory.getUserService();
User user = userService.getCurrentUser();
When the user is authenticated , I need to get the documents from google docs. I know I have to use OAuth, but I could not use it correctly.
I hope some one can help me, Thanks.
if i have already a user logged in how can i get his docs
Here is a good reference with way more information than I could provide myself:
http://code.google.com/apis/documents/docs/3.0/developers_guide_java.html
That should get you on the right path.
The user has authenticated to App Engine, but they have not authenticated against any other services, or provided you with access to them. You cannot use their App Engine credentials to access any other services; you will need to follow the standard OAuth authorization procedure in order to access any other services owned by that user.
first do the oauth part using for example signpost library : here is an example
( look at there sample code for Google)
after you complete oauth process in step 1; and store the ACCESS_TOKEN and
TOKEN_SECRET you can access google docs service like code snippet below .
(note : CONSUMER_KEY & CONSUMER_SECRET are the ones you get from google and used in step 1 . ACCESS_TOKEN & TOKEN_SECRET are the secret access tokens sent to you by google auth server after you complete oauth process in step 1)
hope this helps.
GoogleOAuthParameters oauthParameters = new GoogleOAuthParameters();
oauthParameters.setOAuthConsumerKey(CONSUMER_KEY);
oauthParameters.setOAuthConsumerSecret(CONSUMER_SECRET);
oauthParameters.setOAuthToken(ACCESS_TOKEN);
oauthParameters.setOAuthTokenSecret(TOKEN_SECRET);
DocsService client = new DocsService("yourCompany-YourAppName-v1");
client.setOAuthCredentials(oauthParameters, new OAuthHmacSha1Signer());
URL feedUrl = new URL("https://docs.google.com/feeds/default/private/full");
DocumentListFeed resultFeed = client.getFeed(feedUrl, DocumentListFeed.class);
for (DocumentListEntry entry : resultFeed.getEntries()) {
System.out.println(entry.getTitle().getPlainText());
}