How to use Google Drive for .net application? - asp.net-core

I've got a .net core application running on the server and I would like to store my app data directly on Google Drive. I use regular Google account for it with Google Drive API enabled (it seems to be enabled) and service account with credentials stored in json file. Here's code to create Google Drive service in my application:
private DriveService CreateDriveServiceClient() => new DriveService(
new BaseClientService.Initializer()
{
HttpClientInitializer = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(this.config.ClientEmail)
{
Scopes = new string[]
{
DriveService.Scope.DriveAppdata,
},
}.FromPrivateKey(this.config.PrivateKey))
});
It uses scope https://www.googleapis.com/auth/drive.appdata, which leads to string drive.appdata
However I get the following error:
Insufficient Permission: Request had insufficient authentication scopes.] Location[ - ] Reason[insufficientPermissions] Domain[global]
when trying to acces at least client.Drives.List().Execute();
It seems to me that I need to add a role to access drive.appdata scope. But I can't find an appropriate role in Google console, neither I can't find this scope when creating a new role for service account
How can I add permissions to service account to use Google Drive?

According to the Google Drive API v3 documentation:
This request requires authorization with at least one of the following scopes:
https://www.googleapis.com/auth/drive
https://www.googleapis.com/auth/drive.readonly
Therefore, if you want to be able to use the Drive API properly, you should change the scope to one of those provided above.
You can also test the API on the Google Drive API v3 here.
Furthermore, I suggest you check these links for more information:
Google Drive API v3;
OAuth2.0 Playground.

Related

Unable to set scope games::prime for Login with Amazon

I've been tryin to setup an external login with amazon to integrate prime gaming for my web app in asp net core 3.1. I've been following this Amazon Prime Gaming Integration. I have been successfully stored the amazon userId, its token, refresh token and expires in into my database. However, I'm unable to call a post API request to /api/account/link to sync up my account according to this document. I am getting an error with message: Not all permissions are authorized. I did a bit research and it mentioned in another document link (Tips-Troubleshoot) that I need to set both scopes to games::prime and profile (I only had profile set before).
However, when adding games::prime scope, I get an exception for invalid scope: invalid_scope;Description=An unknown scope was requested
I'm using AspNet.Security.OAuth.Amazon to help with the external login with amazon, and this is the code that I have in my Startup.cs file
services.AddAuthentication()
.AddAmazon(o =>
{
o.ClientId = clientId;
o.ClientSecret = clientSecret;
o.Scope.Add("profile");
o.Scope.Add("games::prime");
o.SaveTokens = true; // get access token and refresh token
o.AccessDeniedPath = new PathString("/Home/WebAppSetting");
})
If I remove games::prime from the scope, then everything works but then I will get an error message Not all permissions are authorized
Can anyone please tell me what I'm missing and not doing correct in here ? I have been searching for amazon documents on their sites, but seems like there is nothing useful there.
Thank you
It turned out that in order to use scope games::prime, you need to contact amazon team to have them unblock that for you. This is not mentioned at all in the push integration documentation.

Azure SQL authenticate as user via API with MS Identity Platform

I have an ASP.NET Core 3.1 Web App calling an ASP.NET Core 3.1 Web API, which in turn accesses an Azure SQL database. Authentication is provided via MSAL (Microsoft Identity Platform) - i.e. using the relatively new Microsoft.Identity.Web and Microsoft.Identity.Web.UI libraries.
The goal is to ensure that the user pulls data from SQL via the API under the context of his/her own login, thus enabling row-level security, access auditing and other good things.
I have succeeded in getting the sign-in process to work for the Web App - and through that it obtains a valid access token to access the API using a scope I created when registering the latter with AD.
When I run both the API and the App locally from Visual Studio everything works as expected - the correct access tokens are provided to the App to access the API, and the API to access SQL - in both cases under the user's (i.e. my) identity.
When I publish the API to App Services on Azure, however, and access it there either from a local version of the Web App or an App-Services hosted version of it, the access token that the API gets to access SQL contains the API's Application Identity (system-assigned managed identity), and not the user's identity. Although I can access SQL as the application, it's not what we need.
The Web App obtains its access token using the GetAccessTokenForUserAsync method of ITokenAcquisition - taking as a parameter the single scope I defined for the API.
The API gets its token (to access SQL) like so:
var token = await new AzureServiceTokenProvider().GetAccessTokenAsync("https://database.windows.net", _tenantId)
...where _tenantId is the tenant ID of the subscription.
I have added the SQL Azure Database "user_impersonation" API permission to the AD registration for the API - but that has not helped. As an aside, for some reason Azure gives the full name of this permission as https://sql.azuresynapse.usgovcloudapi.net/user_impersonation - which is slightly alarming as this is just a UK-based regular Azure account.
I have found a few similar posts to this, but mostly for older versions of the solution set. I'm hoping to avoid having to write my own code to post the token requests - this is supposed to be handled by the MSAL libraries.
Should I somehow be separately requesting a SQL access scope from the Web App after sign-in, or should the API be doing something different to get hold of a SQL access token that identifies the user? Why does it work perfectly when running locally?
It seems like this should be a very common use case (the most common?) but it is barely documented - most documentation I've found refers only to the application identity being used or doesn't tell you what to do for this particular tech stack.
Finally - success! In the end this was the critical piece of documentation: Microsoft identity platform and OAuth 2.0 On-Behalf-Of flow - the key points being:
The App only asks for a token to access the API.
The API then requests a token, on behalf of the user identified via the 1st token, to access SQL.
The key is that - since the API cannot trigger a consent window for the second step - I had to use the Enterprise Applications tab in the Azure portal to pre-grant the permissions for SQL.
So the good news is it does work: maybe it's obvious to some but IMO it took me far too long to find the answer to this. I will write up a fuller explanation of how to do this in due course as it can't only be me struggling with this one.
The bad news is that - in the course of my investigations - I found that Azure B2C (which is the next thing I need to add in) doesn't support this "On Behalf Of" flow - click here for details. That's a great shame as I think it's the most obvious use case for it! Oh well, back to the drawing board.
I'm currently working on a similar problem, using a Net5.0 Web app. The reason it appears to be working locally is you are signed into Visual Studio with a user who can access Azure SQL and those are the rights you get in the Db. The IDE is using those credentials in place of the Managed Service Identity, the latter gets used when you upload the app to Azure.
As you noted, in the App registration you need to grant permission to the App for Azure SQL Database user_impersonation.
In your code, you need to request a token from https://database.windows.net//.default (note the // as it's needed for v1 endpoints). By referencing /.default you are asking for all permissions you've selected for the app in the app registration portal.
https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-permissions-and-consent#the-default-scope
In Startup.cs you need to EnableTokenAcquisitionToCallDownstreamApi with the scope you require.
services.AddMicrosoftIdentityWebAppAuthentication(Configuration)
.EnableTokenAcquisitionToCallDownstreamApi(new[]
{"https://database.windows.net//.default"})
// Adds the User and App InMemory Token Cache
.AddInMemoryTokenCaches();
services.AddAuthorization(options =>
{
// By default, all incoming requests will be authorized according to the
// default policy
options.FallbackPolicy = options.DefaultPolicy;
});
services.AddDbContext<MyDatabaseContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("MyAzureConnection")));
// The database interface
services.AddScoped<ITodos, TodoData>();
services.AddRazorPages()
.AddRazorRuntimeCompilation()
.AddMvcOptions(o =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
o.Filters.Add(new AuthorizeFilter(policy));
})
.AddMicrosoftIdentityUI();
You also need to decorate your controllers with [AuthorizeForScopes(Scopes = new string[]{"https://database.windows.net//.default"}] and include the required scopes for that Controller. For Razor, it's at the top of the page model and requires a reference to `using Microsoft.Identity.Web;'
namespace ToDoApp.Pages.Todos
{
[AuthorizeForScopes(ScopeKeySection = "AzureSQL:BaseUrl")]
public class CreateModel : PageModel
I'm using a section in my appsettings.json for the scope and retrieving it using ScopeKeySection:
"AzureSQL": {
"BaseUrl": "https://database.windows.net//.default",
"Scopes": "user_impersonation"
}
This shows you where to include it for MVC, Razor and Blazor:
https://github.com/AzureAD/microsoft-identity-web/wiki/Managing-incremental-consent-and-conditional-access#in-mvc-controllers
Finally, your DbContext needs a token which you could pass to it from the client app (perhaps...).
This is how I am doing it at the moment
public class MyDatabaseContext : DbContext
{
private readonly ITokenAcquisition _tokenAcquisition;
public MyDatabaseContext (ITokenAcquisition tokenAcquisition,
DbContextOptions<MyDatabaseContext> options)
: base(options)
{
_tokenAcquisition = tokenAcquisition;
string[] scopes = new[]{"https://database.windows.net//.default"};
var result = _tokenAcquisition.GetAuthenticationResultForUserAsync(scopes)
.GetAwaiter()
.GetResult();
token = result.AccessToken;
var connection = (SqlConnection)Database.GetDbConnection();
connection.AccessToken = result.token;
}
This is a flawed solution. If I restart the app and try to access it again I get an error Microsoft.Identity.Web.MicrosoftIdentityWebChallengeUserException: IDW10502: An MsalUiRequiredException was thrown due to a challenge for the user
It seems to be related to the TokenCache. If I sign out and in again or clear my browser cache the error is resolved. I've a workaround that signs the app in on failure, but it's deficient since I'm using the app's credentials.
However, it successfully connects to the Azure SQL Db as the user instead of the App with the user's rights instead. When I do solve the error (or find one) I will update this answer.

Upload file to Microsoft OneDrive using graph SDK using asp.net core

I am trying to upload a file to OneDrive using Graph SDK for .Net Core from worker service.
Basically, some files are created at random time and those files needs to be uploaded to specified path on OneDrive from worker service at midnight every day.
I have following information stored in appconfig.json file in application:
ClientID
ClientSecret
TenantID
I have checked samples on various sites but could not find how to upload files using above ID and Secret. I believe there must but some kind of authProvider that I could initialize using above ID and Secret.
I also checked miscrosoft's documentation but coudl not find any example on how to upload file using SDK with ID and Secret.
https://learn.microsoft.com/en-us/onedrive/developer/rest-api/api/driveitem_put_content?view=odsp-graph-online
Additional Point
Upload new file
Overwrite if file already exists(hope that graph already supports this)
file size is < 4MB
path format /folder1/folder2/filename.pdf
Any help would be appreciated.
Remember to assign Application permission (Client credentials flow) to the app registration. See Application permission to Microsoft Graph.
You can use Client credentials provider.
IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder
.Create(clientId)
.WithTenantId(tenantID)
.WithClientSecret(clientSecret)
.Build();
ClientCredentialProvider authProvider = new ClientCredentialProvider(confidentialClientApplication);
Upload small file (<4M):
GraphServiceClient graphClient = new GraphServiceClient(authProvider);
using var stream = new System.IO.MemoryStream(Encoding.UTF8.GetBytes("The contents of the file goes here."));
await graphClient.Users[{upn or userID}].Drive.Items["{item-id}"].Content
.Request()
.PutAsync<DriveItem>(stream);
If you want to upload large file, see Upload large files with an upload session.
UPDATE:
Use Install-Package Microsoft.Graph.Auth -IncludePrerelease in Tools -> NuGet Package Manager -> Package Manager Console.
It supports both uploading a new file and replacing an existing item.
For how to get the items id, please refer to Get a file or folder.
For example, get the item id of /folder1/folder2 (have a quick test in Microsoft Graph Explorer):
GET https://graph.microsoft.com/v1.0/users/{userID}/drive/root:/folder1:/children
It will list all the children in folder1, including folder2. Then you can find item id of folder2.
UPDATE 2:
Client credentials provider is application identity while all the providers below are user identity.
Since you want to access personal OneDrive (including user identity), you could choose Authorization code provider or Interactive provider.
Authorization code provider: you need to implement interactively sign-in for your web app and use this provider.
Interactive provider: you can easily use this provider to implement interactively sign-in in a console app.
You can have a quick test with the second provider.
Please note that you should add the Delegated (personal Microsoft account) permissions into the app registration. See Delegated permission to Microsoft Graph.
And in this case, you should modify all graphClient.Users[{upn or userID}] to graphClient.Me in your code.
I'm afraid that you have to implement sign-in interactively auth flow to access personal OneDrive.

Connecting within ArcGIS application with resource

I have following dillema:
Using ArcGIS Enterprise 10.8, I have added a new item – Application – to a users content.
This generates an Application item, with an App ID and APP Secret, along with App type and redirect URIs defined.
These can be used to generate an access token via the OAUTH2 token endpoint:
https:///sharing/rest/oauth2/token
using the parameters :
client_id=APPID&
client_secret=APPSECRET&
grant_type=client_credentials
ESRI States in their documentation:
“Successful authentication directly returns a JSON response containing the access token that allows the application to work with resources that are accessible to the application (that is, have been shared with the application). Use of the client_secret as previously described is mandatory.”
Question is: how do we share resources with the application?
The overall goal is to grant an external application (unknown user) access to portal ressources (ie.a layer item) via OAUTH2 app login.
Do you have any suggestions?
This is certainly confusing documentation, but I have found it useful to review this page: Limitations of App Login.
Specifically:
Applications cannot create, update, share, modify, or delete items
(layers, files, services, maps) in ArcGIS Online or ArcGIS Enterprise.
... If you want to access private content within an organization or
content that has been shared with a user, you must use the named user
login pattern for authentication.
For what you want to do, you'll most likely want to create a non-expiring refresh token based on a specific user, and store that in with your external application.

How to implement Google Drive API with .Net Core MVC Identity

I have been trying to get Google Drive API working with .Net Core Identity (which has google Oauth2 built in)
I have tried following this tutorial. But it is not for .Net Core. How would I use .Net Core Identity to be able to access google drive after they log in?
You'll need your Google console app to have the Drive API enabled then you'll need to add the correct scope to your Identity configuration in startup.cs. This will ensure that when your user logs in they get the correct scope assigned to their login token(s).
An example, if you want to read files and/or file META from Drive you might have:
https://www.googleapis.com/auth/drive.readonly
See here for scopes: https://developers.google.com/drive/api/v2/about-auth
Here is a sample of how that might look:
services.AddAuthentication().AddGoogle(googleOptions =>
{
googleOptions.ClientId = "YOUR_CLIENT_ID";
googleOptions.ClientSecret = "YOUR_CLIENT_SECRET";
googleOptions.Scope.Add("https://www.googleapis.com/auth/drive.readonly");
googleOptions.SaveTokens = true;
...
});
From here your user will have an AccessToken and RefreshToken returned when they log in. You can use this (along with their email address) to access their Google Drive.
I have a service which I use to make various requests to the API.