I am building a chatbot using FreshDesk that will connect to Google Calendar, so I
go to Google Console API and create a project
add the google calendar API service to the project
get the credentials (OAuth client ID)
Configure the OAuth consent screen, adding sensitive scope (edit, view, delete)
develop my own program which will provide the API to book room using google calendar API.
It still fine here, everything works fine in my local computer. Until I tried to deploy to the server on AWS.
The problem I encountered:
According to the sample code from Google calendar API document (https://developers.google.com/calendar/api/quickstart/dotnet)
When I trigger the API at the first time in my local computer through Postman, Chrome will pop-up automatically and display the consent screen and a file
"Google.Apis.Auth.OAuth2.Responses.TokenResponse-user" will be generated after I clicked confirm in the screen.
But the consent screen did not pop out after I deploy to AWS server. I need to copy the Google.Apis.Auth.OAuth2.Responses.TokenResponse-user in my local computer to the AWS server. Is it a proper way to do that? And the file will somehow disappear.. then all of my API is broken without this auth file.. What is the best way to deploy my program to production?
Your going to have a few issues here. First off GoogleWebAuthorizationBroker.AuthorizeAsync is designed for installed applications. It opens the consent screen on the machine its running on. So if its running on AWS then its going to try to open the consent screen on AWS which means you the user can not see the consent screen. The way around this is to run your code once then FileDatastore will store your credentials in the Google.Apis.Auth.OAuth2.Responses.TokenResponse-user file on your machine.
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
Scopes,
"user",
CancellationToken.None,
new FileDataStore(credPath, true)).Result;
your code will then read from Google.Apis.Auth.OAuth2.Responses.TokenResponse-user and load the refresh token when ever it needs to.
The issues you are going to have is that there will be no way for your code to request that you authorize it again if the Refresh token expires. Refresh toekns can expire but its rare after the application has been set to prodcution in Google cloud console.
service account
You may want to consider using a service account if you use a service account you will need a google workspace account to set up domain wide delegation and the service account will only be able to access a calendar on the workspace domain.
The nice thing about service accounts is that they are pre authorized though domain wide delegation which means you wont need to workie about the refresh token, verifying the app, or the consent screen.
Related
I need help.
I need to create an app, which takes all events from different calendars from my company, and display them in computers in conferences rooms. This is created, works good. To authenticate I use Oauth 2.0 like google wants, but I tested it only on my computer. When the app was launched on the computer in the conference room, the app needed logging into google account, which surprised me, because I put my oauth 2.0 credentials into my code, so I thought that this would be enough.
How can I skip that part, to authenticate only from code level and not display Oauth popup message to user?
When you run your code locally you are authorizing it. If you are using the official Google api .net client library then it is storing your authorization credentials in the %appdata% folder on your machine. Once you move this to the computers in the conference rooms they have not been authorized and there for will require that you authorize them. So you should be able to just run it once on each machine and authorize it and it will be all set.
If you do however have a google workspace account, I would recommend you look into using a service account and configuring domain wide delegation this would stream line your process a bit.
The following example shows how to use a service account with domain wide deligation.
var credential = GoogleCredential.FromFile(PathToServiceAccountKeyFile)
.CreateWithUser("user#yourdomain.com") // delegate to user on workspace.
.CreateScoped(new[] {CalendarService.ScopeConstants.Calendar});
I'm having an ASP.Net MVC app configured to use google authentication. I'm having another app which is also configured to use google SSO.
When navigating first app to second app when multiple user active google logins are available, I do not need the user to select the google account from choose account screen. I'm expecting do a token based authentication in the second application so that the app tp app navigation is seamless and the second app will authenticated with the correct google account.
Is there a way to do it or possible documentation or guide on how to do it.
I have a C# class library from which I am trying to add an event to someone calendar just by using his/her email address and password as credentials. So I debug it and once started a new page in the internet browser is open and below error is displayed:
Below the code:
// It crashes when calling GoogleWebAuthorizationBroker.AuthorizeAsync
UserCredential credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
new ClientSecrets
{
ClientId = "myGoogleAccount#gmail.com",
ClientSecret = "myGoogleAccountPasswordHere",
},
new[] { CalendarService.Scope.Calendar },
System.Environment.UserName,
CancellationToken.None).Result;
// Create the service.
var service = new CalendarService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "Calendar API Sample",
});
Why this error is happening? ClientId is not the gmail account?
Also why a new page in internet browser is opened? I want to do authentication without opening a page in the internet browser because this class library is called from a windows service so I need authentication to be done in the background.
Answer:
In order to insert methods into a user's Calendar, you need the user to give your application permission to make actions on their behalf. This is done using a Google Cloud Platform (GCP) Project, with OAuth2 Authentication.
More Information:
Each application which runs and takes actions on behalf of a Google account user has to have the scope of its ability well defined so that it can't start doing things that a user hasn't given it permission to do.
For example: if you give an application permission to create Calendar events, you don't want it to be able to do other things such as read your emails or download the contents of your Drive.
In order to designate what your application has the power to do, it needs to be registered with Google. As you have already deduced in your question and comments, the Client ID and Client Secret required by an application connecting to a G Suite API isn't simply the username and password of a Google Account, but a designated ID-secret ID pair which is provided by Google to identify your application.
OAuth2:
OAuth2 is a specific authorisation framework. The framework is defined in RFC 6749 and sets out the process in which a user can authorise an application to access their account. The limit of the authorisation is defined by the scope of the application on authorisation, and can not be changed without explicit re-authorisation by the user.
Before continuing it's worth defining a few important terms here:
User:
A user is the person; the individual that has an account and gives permission for an application to take actions on their behalf.
Client or Application:
A Client or Application is a program which is designed to take actions over HTTP by connecting to a service's API. Applications can be mobile apps, web apps or desktop clients.
Authorisation Server:
An Authorisation server is a server which is separate from the servers that store user resources. It verfies the user's identity and provides a grant which can be used to get an access token to a resource server.
Resource Server:
This is the server where user data is stored. This could be anything from user information to files or emails.
The authorisation flow has already been well documented, but for the sake of this scenario we can abstract it down to the following steps:
An Application wishes to take an action on a resource server on behalf of a user.
The Application makes an authorisation request to the user. This is generally presented as a login page for the account for which the application is accessing.
The user logs in to their account and is presented with an OAuth consent screen - this contains information such as the application's name, and the list of tasks that it is requesting authorisation for. These are often generic, and will say something like See and download all your Google Drive files or View and edit events on all your calendars. This allows the user to know what they are authorising before they confirm.
An Authorisation Grant is given to the application.
The Application provides the obtained authorisation grant along with its assigned client credentials to an authorisation server.
On verifying that both the user's grant and the client's credentials are correct, the authorisation server returns an access token which can be used to access the requested and approved resources. Note: This is normally all handled by your client library for whichever language you use.
The Application can now make a request to the resource server, providing the access token obtained from the authorisation flow. It is at this point that the permitted resources can be accessed.
Google Cloud Platform Projects:
A GCP project what Google sees as your application. The registration for your application is required to be able to obtain the client ID and client secret which your application will need in order to get an access token in the authorisation flow.
In the GCP console you can set up all the required services that your application needs. Each API you wish to use has to be enabled for your application, as there are many Google services with APIs and they are disabled by default.
Once a GCP Project has been created, you can use the API Library (From the ≡ > APIs & Services > Library menu item on the left) to find and enable the API. Note that for your use case you will want to enable the Google Calendar API and not the CalDAV API.
You will also need to set up a consent screen before obtaining credentials for your application. An OAuth consent screen is what your users will be presented with in the first step of the OAuth flow:
When setting up your OAuth consent screen, you will need to provide the following information:
Application type (public or internal to your domain)
Application name
The scopes that your application needs (explained in the next section)
After the consent screen has been set up, you can download the client credentials for your application. With these, your application has permission to run as a client, but each user that has their resources accessed will still have to give their explicit permission to allow the application to do so.
Scopes:
Within a single API there can be many scopes of access - having read-only access to calendar events is vastly different to having complete read-write access to all calendars that a user owns. This is where scopes come into play.
A scope is defined as its namesake; that is to say, a scope defines the scope of access an application has to a service. Even though an entire API has been enabled for a project doesn't mean that you need to use all features of the API. For this reason, scopes need to be defined.
Scopes are defined in the application itself before making the initial request for the user grant. In C#, for example (taken from the .NET Calendar API Quickstart):
// scopes are defined as an array of strings:
static string[] Scopes = { CalendarService.Scope.CalendarReadonly };
...
UserCredential credential;
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
Scopes,
"user",
CancellationToken.None,
new FileDataStore(credPath, true)).Result;
The access token that is stored is based on the scopes that were defined in the call. If a method is called which needs a different scope to those which the token grants access to, the call will fail with a 403: Unauthorized error. The required scope will need to be added to the application, the old access token deleted and the user will need to grant permission for the new scopes.
Service Accounts:
As well as regular users, there is another special type of Google account called a Service Account. From the documentation:
A service account is a special kind of account used by an application or a virtual machine (VM) instance, not a person. Applications use service accounts to make authorized API calls.
Normally, every user for whom you wish to perform tasks or access resources needs to give explicit permission for your application to do so. For G Suite domains, however, you can use a service account with domain-wide delegation to complete tasks on behalf of users without the requirement.
Service accounts use a special kind of service-account credential which can be created in GCP and used in your application. Rather than making a UserCredential object, a ServiceAccountCredential is needed which doesn't require involvment by an end user.
When running a service account on behalf of a user with domain-wide delegation, the name of the user needs to be specified in the delegated credentials so the application knows which user in the domain to run as. If a user is not provided, the service account will run the code as itself; which is useful in some cases but often times will not return an error and so it may not be clear for whom the operation was run.
Note: While Service Accounts can be created by anyone, domain-wide delegation of authority can only be accomplished for a G Suite domain, and not #gmail.com addresses. All Gmail account users must give explicit permission for an application to run on thier behalf as set out by the OAuth flow.
References:
Using OAuth2 to Access Google APIs | Google Identity Platform
RFC 6749 - The OAuth2 Authorization Framework
OAuth - Wikipedia | #OAuth2
Google Cloud Platform Console
Google APIs Explorer | Google Developers
.NET Quickstart | Calendar API | Google Developers
Service Accounts | Cloud IAM Documentation | Google Cloud
Perform G Suite Domain-Wide Delegation of Authority | Directory API
OAuth 2.0 | API Client Library for .NET
Related Questions:
Google API Service Account. Can only see service accounts drive even with Domain Wide Delegation Access
Creating events using the Google Calendar API and Service Account
I'm trying to create a application service to be ran in a cloud server (AppHarbor) I do not directly handle, as a C# Console Application. Basically, it is a Telegram Bot that needs to access Gmail and Google Calendar. When running it locally, it propts the user via browser to give access to the account the first time.
Unfortunately, in the server I cannot give that access, so I need a way to login (authentication) directly, without need for authorization.
I've seen the option to use a Service Account, but sadly it requires GSuite to configure the user permissions and that needs payment I need to avoid.
using (var stream = new FileStream("credentials.json", FileMode.Open, FileAccess.Read))
{
// The file token.json stores the user's access and refresh tokens, and is created
// automatically when the authorization flow completes for the first time.
string credPath = "token.json";
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
Scopes,
"user",
System.Threading.CancellationToken.None,
new FileDataStore(credPath, true)).Result;
}
I have generated my json file with all the settings and secrets needed, but it still requires user interaction.
Is there any way to do so without user prompt?
USER
The term user denotes the owner of the data or the account you wish to access. In order to access data owned by a user account. You must have permission of the user in question.
Service Account
Service accounts are only indented for user when you the developer have access to the accounts in question. You are correct that you can only use them with gmail if the emails are controlled though a gsuite other wise there is no way to preauthorize them. service accounts
Oauth2 refresh token
I have done something like this in the past. What you are going to need is two applications. One which your users can run to authenticate your application and send the credentials to your server and the second is the console application you have now. Oauth
User Application
The user application should either be a web application or an installed application that the users can run. They run this application grant your application access (remember to add the offline scope) You will get a refresh token back. Take this refresh token and send it to the server that is running your console application.
Console application
your console application should then use these refresh tokens to request a new access token and gain access to the users data when ever it needs to.
To load this refresh token you will need to create your own implementation of IDataStore. The code you are using now usees FileDataStore which stores the crednetials in %appdata% you will need to over ride that so that it can read from where ever it is you had the user application store the data. I have a few examples here datastore.cs gist
Application verification
remember that you will need to have your application verified by google before you can release it GMAIL is one of the harder scopes to have approved you may want to start that process early.
I have created a custom OData feed using ASP.NET Web API. This service is configured using Azure Active Directory for Authentication. The issue I currently have is when I try and connect Power BI or Power Query up to the OData feed. Once I have entered my credentials I get the following error:
Invalid_resource: AADSTS50001: The application named https://localhost:44320 was not found in the tenant named XXX.onmicrosoft.com. This can happen if the application has not been installed by the administrator of the tenant or consented to by any user in the tenant. You might have sent your authentication request to the wrong tenant.
I'm pretty sure that I have configured AAD correctly as I can connect up through the web browser with no issues. I'm not sure if it's even possible to connect up with Power Query as I have seen conflicting posts in various forums!
Any help would be greatly appreciated.
I also experienced this error when trying to use PowerQuery to get API data into an Excel spreadsheet, and had a hard time getting past it mainly as the documentation around all this is somewhat slim.
Problem - How to import data from an API in Azure into an Excel spreadsheet?
I had a ASP.NET API running in Azure and exposed on my own domain at a URL e.g. https://api.myapp.net (rather than a built-in azure URL). This API was hooking into Azure AD via the OWIN middleware: UseWindowsAzureActiveDirectoryBearerAuthentication, i.e. the App Service in Azure had AAD authentication turned OFF. The application was registered in AAD as multi-tenant, in a different tenant to the where the app service resource was hosted.
Issue 1 - Credentials
So first off in Excel do Data > From Web > https://api.myapp.net/Products and select "Organisational Account" and click "Sign-in", giving this error:
There are two different fixes for this:
1. Return WWW-Authenticate response header on all 401s
If you have enabled AAD on your API in code using the owin middleware, then you need to ensure the service returns the correct WWW-Authenticate header in the 401 response to the client, specifically we must specify the AAD sign-in end-point as the authorization uri, e.g.:
WWW-Authenticate: Bearer realm="",
authorization_uri="https://login.microsoftonline.com/<<tenant id of your users>>"
See: this TechNet question which suggested this solution
2. Turn on AAD authentication for the API app service in the Azure portal
Alternatively in the Azure Portal for the tenant where the App Services themselves are hosted
Go to App Services and locate the API app service
In Authentication / Authorization turn App Service Authentication ON
For "Action to take when unauthenticated" select "Log in with Azure Active Directory"
Under "Authentication Providers" click "Azure Active Directory" and select "Advanced" settings
Under "Client id" enter the application id for the API app registration
Under "Issuer Url" enter the sign-in end-point for the tenant the users of the API originate from
Under "Allowed Token Audiences" ensure you have added the actual url of your API e.g. https://api.myapp.net
Save the changes
Essentially this config is described here
Issue 2 - App Registration
Now back in Excel when you click sign-in on the query a pop-up will open and take you to the Microsoft sign-in page for the tenant you configured. When you enter credentials and sign-in you may then get this error (the one in the question):
To fix this issue you need to ensure the the application is registered correctly with AAD.
Here is how...
In the Azure Portal for the tenant where your applications are registered
Go to Azure Active Directory > App Registrations and locate the registration for the API service
Edit the Manifest and ensure the actual deployed API URL is configured in the list of identifierUris e.g. https://api.myapp.net (there will be an Azure built-in URL already configured)
{
"identifierUris": [
"https://api.myapp.net",
"https://<mytenant>.onmicrosoft.com/<myappregname>"
]
}
If the application is multi-tenant you will need to ensure the domain used in this URL is verified with Azure
You must also ensure the user_impersonation scope is available for the application:
{
"oauth2Permissions": [
{
"adminConsentDescription": "Allow the application to access myapp on behalf of the signed-in user.",
"adminConsentDisplayName": "Access myapp",
"id": "xxxxx-xxx-xxx-xxx-xxxxxxx",
"isEnabled": true,
"lang": null,
"origin": "Application",
"type": "User",
"userConsentDescription": "Allow the application to access my on your behalf.",
"userConsentDisplayName": "Access my app",
"value": "user_impersonation"
}
]
}
Save changes.
Issue 3 - Allowed token audience
Now back in Excel, you should be able to get past the sign-in but when clicking on "Connect" you may get this error:
Now looking in fiddler you will see the AAD login works and returns a token but when this is sent to the API you get a 401.
This is only an issue if you have enabled AAD via code rather than through the Azure portal (see Issue 1 above!). To fix it you need to ensure the TokenValidationParameters class passed to the owin middleware has ValidAudience set to the actual url of your deployed API.
Run the query
With all that set-up everything should now work, back in Excel ...
Click sign-in a pop-up will open and take you to the Microsoft sign-in page for the tenant you configured, sign-in with you credentials
Click Connect
PowerQuery editor will then open and display the retrieved data from the API
Click Home > Advanced Editor you will be able to view the raw query - this is in M-query syntax the query language used by PowerQuery, in my case the data was a flat array so this sufficed:
let
Source = Json.Document(Web.Contents("https://api.myapp.net/Products")),
#"Converted to Table" = Table.FromRecords(Source)
in
#"Converted to Table"
Click Close & Load to return the data to the Excel worksheet
How this works
In case you care (and are still reading this!), the way this works seems to be:
PowerQuery requests access to your API under the "Microsoft PowerQuery For Excel" built-in application (client id a672d62c-fc7b-4e81-a576-e60dc46e951d)
When you sign-in AAD grants the dynamic scope user_impersonation on your API (identified by the resource URL https://api.myapp.net) to the "Microsoft PowerQuery For Excel" app
You can see this in the portal by going to Enterprise Applications, checking Microsoft Applications and searching for Microsoft PowerQuery For Excel
Sorry for the long post but hopefully this helps somebody do something seemingly quite trivial - pull data from an API in Azure into Excel!
I have managed to get past this issue by publishing my WebAPI to an Azure Web App. Interestingly, when its hosted on Azure it prompted to allow "Power Query for Excel" access. The issue could be limited to the fact I was running it on IIS Express on my Dev box.