On Google AppEngine i have been through authentication for Google Docs ... access using AuthSub authshub.
We managed to AuthSubUtil.exchangeForSessionToken(..).
QUESTION: Is it possible to follow up and get Access to Google Drive using this token?
... new Drive.Builder(httpTransport, jsonFactory, credential);
I'm not sure if AuthSub works with the Google Drive API, but if it does, this code snippet should resolve your problem:
new Drive.Builder(httpTransport, jsonFactory, new HttpRequestInitializer() {
#Override
public void initialize(HttpRequest request) {
HttpHeaders headers = request.getHeaders();
// Not sure if this test is necessary as headers might never be null.
if (headers == null) {
headers = new HttpHeaders();
request.setHeaders(headers);
}
headers.setAuthorization("AuthSub token=\"<TOKEN>\"");
}
}).build();
Important: please be aware that AuthSub is being deprecated and you should try to migrate your application to use OAuth 2.0 as soon as you can.
Related
I have been playing around with Managed Service Identity in Azure Logic Apps and Azure Function Apps. I think it is the best thing since sliced bread and am trying to enable various scenarios, one of which is using the MSI to get an app-only token and call into SharePoint Online.
Using Logic Apps, I generated a managed service identity for my app, and granted it Sites.readwrite.All on the SharePoint application. When then using the HTTP action I was able to call REST endpoints while using Managed Service Identity as Authentication and using https://.sharepoint.com as the audience.
I then though I'd take it a step further and create a function app and follow the same pattern. I created the app, generated the MSI, added it the Sites.readwrite.All role same way I did with the Logic App.
I then used the code below to retrieve an access token and try and generate a clientcontext:
#r "Newtonsoft.Json"
using Newtonsoft.Json;
using System;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using Microsoft.SharePoint.Client;
public static void Run(string input, TraceWriter log)
{
string resource = "https://<tenant>.sharepoint.com";
string apiversion = "2017-09-01";
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add("Secret", Environment.GetEnvironmentVariable("MSI_SECRET"));
var response = client.GetAsync(String.Format("{0}/?resource={1}&api-version={2}", Environment.GetEnvironmentVariable("MSI_ENDPOINT"), resource, apiversion)).Result;
var responseContent = response.Content;
string responseString = responseContent.ReadAsStringAsync().Result.ToString();
var json = JsonConvert.DeserializeObject<dynamic>(responseString);
string accesstoken = json.access_token.ToString()
ClientContext ctx = new ClientContext("<siteurl>");
ctx.AuthenticationMode = ClientAuthenticationMode.Anonymous;
ctx.FormDigestHandlingEnabled = false;
ctx.ExecutingWebRequest += delegate (object sender, WebRequestEventArgs e){
e.WebRequestExecutor.RequestHeaders["Authorization"] = "Bearer " + accesstoken;
};
Web web = ctx.Web;
ctx.Load(web);
ctx.ExecuteQuery();
log.Info(web.Id.ToString());
}
}
The bearer token is generated, but requests fail with a 401 access denied (reason="There has been an error authenticating the request.";category="invalid_client")
I have tried to change the audience to 00000003-0000-0ff1-ce00-000000000000/.sharepoint.com#" but that gives a different 401 error, basically stating it cannot validate the audience uri. ("error_description":"Exception of type 'Microsoft.IdentityModel.Tokens.AudienceUriValidationFailedException' was thrown.). I have also replace the CSOM call with a REST call mimicking the same call I did using the Logic App.
My understanding of oauth 2 is not good enough to understand why I'm running into an issue and where to look next.
Why is the Logic App call using the HTTP action working, and why is the Function App not working??
Anyone?
Pretty basic question: Have you tried putting an '/' at the end of the resource string e.g. https://[tenant].sharepoint.com/ ?
This issue is also present in other endpoints so it may be that its interfering here as well.
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.
using this sample:
https://github.com/Azure-Samples/active-directory-dotnet-webapp-webapi-openidconnect
This works as expected when running it locally
But when we deploy it (azure web app), it still authenticates, but the OpenIdConnectAuthenticationNotifications.AuthorizationCodeReceived event is not firing.
This the code.
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = Authority,
PostLogoutRedirectUri = redirectUri,
RedirectUri = redirectUri,
Notifications = new OpenIdConnectAuthenticationNotifications()
{
AuthorizationCodeReceived = OnAuthorizationCodeReceived,
AuthenticationFailed = OnAuthenticationFailed
}
});
private async Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedNotification context)
{
var code = context.Code;
ClientCredential credential = new ClientCredential(clientId, appKey);
string userObjectID = context.AuthenticationTicket.Identity.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
AuthenticationContext authContext = new AuthenticationContext(Authority, new NaiveSessionCache(userObjectID));
Uri uri = new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path));
AuthenticationResult result = await authContext.AcquireTokenByAuthorizationCodeAsync(code, uri, credential, graphResourceId);
}
This is a problem because it requires caching of the token to make an outbound call.
Since it doesn’t have it, it throws.
There was an issue that caused this related to a trailing slash after the redir url but we’ve already tried that.
So two questions…
1) Under what conditions would event get fired and why would this work when running locally? According to the docs it should be "Invoked after security token validation if an authorization code is present in the protocol message."
2) What is the best way to debug this? Not clear on what to look for here.
1) Under what conditions would event get fired and why would this work when running locally? According to the docs it should be "Invoked after security token validation if an authorization code is present in the protocol message."
As the document point, this event will fire when the web app verify the authorization code is present in the protocol message.
2) What is the best way to debug this? Not clear on what to look for here.
There are many reason may cause the exception when you call the request with the access_token. For example, based on the code you were using the NaiveSessionCache which persist the token using the Sesstion object. It means that you may also get the exception when you deploy the web app with multiple instance. To trouble shoot this issue, I suggest that you remote debug the project to find the root cause. For remote debug, you can refer the document below:
Introduction to Remote Debugging on Azure Web Sites
I am working on a product that is supposed to be installed in Google App Engine.
In this I am using Service account for authenticating Gmail API, Drive API, Calendar API etc.
Its working fine with downloaded P12 file as authentication. But as its product I don't want client to download and upload on app on every install.
Can there be a way to authenticate it without privatekey file or using that API without service account.
In below page its mentioned that there is System-managed key-pairs are managed automatically by Google. Can it be helpful? I did't find any example of it.
https://cloud.google.com/iam/reference/rest/v1/projects.serviceAccounts.keys
In below link it suggest that for Google Cloud Platform I should use Google Managed Key
https://cloud.google.com/iam/docs/understanding-service-accounts
Can this key used without downloaded file ?
Thanks
I could achieve it by IAM API
https://cloud.google.com/iam/reference/rest/v1/projects.serviceAccounts.keys
Below is Java code for it
AppIdentityCredential credential = new AppIdentityCredential(
Arrays.asList("https://www.googleapis.com/auth/cloud-platform"));
Iam iam = new Iam(httpTRANSPORT, jsonFACTORY, credential);
try {
Iam.Projects.ServiceAccounts.Keys.Create keyCreate = iam.projects().serviceAccounts().keys()
.create("projects/myProject/serviceAccounts/myProject#appspot.gserviceaccount.com", new CreateServiceAccountKeyRequest());
ServiceAccountKey key = keyCreate.execute();
} catch (IOException e) {
System.out.println(e.getMessage());
}
Any key can be used to generate GoogleCredential as below
InputStream stream = new ByteArrayInputStream(key.decodePrivateKeyData());
GoogleCredential credential = GoogleCredential.fromStream(stream);
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.