I'm trying to connect to my Azure Mysql via http rest api (https://learn.microsoft.com/en-us/rest/api/mysql/) without success. The problem is that i can't get the JSON Web Token from my Web App. Situation:
Azure Web App ----- rest api ----> Azure MySql
I guess i need to 'register' this Mysql Server resource in active directory but seems i can't do it.
I followed this tutorial (https://blogs.msdn.microsoft.com/jpsanders/2017/03/17/accessing-azure-app-services-using-azure-ad-bearer-token-2) but i have the same problem : i can't register MySql in Azure Active Directory .
So, how can i obtain a JSON Web Token for Mysql HTTP REST API ?
Thanks!
-------- AD PROPIETARY ROLE FOR MYSQL RESOURCE (NOT MYSQL SERVER) --
---------------- CODE ----------------------------------------------
//
// https://blogs.msdn.microsoft.com/jpsanders/2017/03/17/accessing-azure-app-services-using-azure-ad-bearer-token-2/
//
public static class AzureActiveDirectory
{
// the AD Authority used for login. For example: https://login.microsoftonline.com/myadnamehere.onmicrosoft.com
public static string authority = "";
// the Application ID of this app. This is a guid you can get from the Advanced Settings of your Auth setup in the portal
public static string clientId = "";
// the key you generate in Azure Active Directory for this application
public static string clientSecret = "";
// the Application ID of the app you are going to call.This is a guid you can get from the Advanced Settings of your Auth setup for the targetapp in the portal
public static string resource = "";
static public async Task<AuthenticationResult> GetS2SAccessTokenForProdMSAAsync()
{
var task = await GetS2SAccessToken(authority, resource, clientId, clientSecret);
return task;
}
static async Task<AuthenticationResult> GetS2SAccessToken(string authority, string resource, string clientId, string clientSecret)
{
var clientCredential = new ClientCredential(clientId, clientSecret);
AuthenticationContext context = new AuthenticationContext(authority, false);
AuthenticationResult authenticationResult = await context.AcquireTokenAsync(
resource, // the resource (app) we are going to access with the token
clientCredential); // the client credentials
return authenticationResult;
}
}
AzureActiveDirectory.authority = "https://login.microsoftonline.com/********/";
AzureActiveDirectory.clientId = "********";
AzureActiveDirectory.clientSecret = "********";
AzureActiveDirectory.resource = "https://management.azure.com/";
try
{
AuthenticationResult token = await AzureActiveDirectory.GetS2SAccessTokenForProdMSAAsync();
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Authorization", "Bearer " + token.AccessToken);
var resp = await client.GetAsync("https://management.azure.com/subscriptions/*******/resourceGroups/MYSQL/providers/Microsoft.DBforMySQL/servers/shoplister/firewallRules?api-version=2017-12-01");
Console.WriteLine(resp.StatusCode.ToString());
Console.WriteLine();
}
catch (Exception e) { Console.WriteLine(e); }
--------------- AFTER CHANGES NOW GETTING UNAUTHORIZED ------------
I'm compiling the important points from our discussion in the comments that led to a solution:
Use https://management.azure.com as the resource identifier when acquiring the access token
Use https://login.microsoftonline.com/tenant-id-here/ as the authority (you can also use a verified domain name instead of the id). This defines which AAD tenant you authenticate against
The access token must be attached with new AuthenticationHeaderValue("Bearer", token.AccessToken) in C#, so that the resulting header is Authorization: Bearer tokengoeshere
Finally make sure you have granted permissions to the right app. There can be apps with an identical or similar name.
Related
I have created the app, was able to debug the source code.
Also received Client ID and Client secret.
I need your help to understand how to get access token.
Basically this part -
enter image description here
It will be really helpful if you can provide a sample code on how to send the HTTP request as I am novice to web API's.
I have added this code to my solution -
enter image description here
enter image description here
Thanks,
STR
Here is a working example, how to obtain an Access token.
You need to add NewtonSoft.Json nuget package to your project to run it.
public class TokenModel
{
[JsonProperty("access_token")]
public string AccessToken;
}
public async Task<string> GetToken()
{
var credentials = new Dictionary<string, string>();
credentials.Add("client_id", "YOUR_CLIENT_ID");
credentials.Add("client_secret", "YOUR_CLIENT_SECRET");
credentials.Add("grant_type", "YOUR_GRANT_TYPE");
credentials.Add("scope", "YOUR_SCOPE");
var content = new FormUrlEncodedContent(credentials);
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri("https://developer.api.autodesk.com");
var response = await client.PostAsync("/authentication/v1/authenticate", content);
if (response.IsSuccessStatusCode)
{
var responseData = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<TokenModel>(responseData).AccessToken;
}
}
}
And simple usage:
var token=await GetToken();
It is not perfect, I've tried to simplify all the moments.
About 6 months ago I set up a web application in the google developers console so that employees of our internal web site could initiate emails which would read a template doc in a google account, merge some fields and then download a pdf version of it to email out.
Now we have to move those template docs to a different google managed domain/user account so I've made copies of the documents in the new account and updated our references with the new doc ids.
In addition, the email I had when I originally created this application in the google dev console is going away as of the first of the year. So I also have to recreate the app under a new account.
I've done that and matched all the settings of the original app. However, when I try to access a document I get the error Google.Apis.Auth.OAuth2.Responses.TokenResponseException: 'Error:"unauthorized_client", Description:"Unauthorized", Uri:""'
I had followed this page in setting up the original user authentication. I know there was a ton of trial and error before I actually got it working and I must be forgetting something. I'm wondering if it's tied to needing to reauthenticate the new app. Although I'm specifying the new clientid and clientsecret from the new app, I don't get the popup asking me to give permission to the app. I would expect with the new credential info that it would open that window asking me to give permission. Here's that file for reference. Any ideas?
public class AuthCallbackController : Google.Apis.Auth.OAuth2.Mvc.Controllers.AuthCallbackController
{
protected override FlowMetadata FlowData => new AppFlowMetadata();
}
public class AppFlowMetadata : FlowMetadata
{
private static readonly IAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = new ClientSecrets
{
ClientId = AwsSecrets.GoogleCreds.ClientId,
ClientSecret = AwsSecrets.GoogleCreds.ClientSecret
},
Scopes = new[] {DriveService.Scope.Drive},
DataStore = new FileDataStore("Drive.Api.Auth.Store")
});
public override IAuthorizationCodeFlow Flow => flow;
public override string GetUserId(Controller controller)
{
return "userid";
}
}
public class GoogleController : TECWareControllerBase
{
private readonly IGoogleCredentialService _gservice;
public GoogleController(IGoogleCredentialService gservice)
{
_gservice = gservice;
}
public async Task<ActionResult> IndexAsync(CancellationToken cancellationToken)
{
var result = await new AuthorizationCodeMvcApp(this, new AppFlowMetadata()).AuthorizeAsync(cancellationToken);
if (result.Credential != null)
{
_gservice.SaveRefreshToken(result.Credential.Token.RefreshToken);
return View();
}
return new RedirectResult(result.RedirectUri);
}
}
I finally found a way to get this working.
First off in this method
private static readonly IAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = new ClientSecrets
{
ClientId = AwsSecrets.GoogleCreds.ClientId,
ClientSecret = AwsSecrets.GoogleCreds.ClientSecret
},
Scopes = new[] {DriveService.Scope.Drive},
DataStore = new FileDataStore("Drive.Api.Auth.Store")
});
I had to change the FileDataStore("Drive.Api.Auth.Store") key to something else like FileDataStore("GoogleAuth")
That forced the authentication to fire up.
Unfortunately, google then complained about an invalid redirect uri. The following code returned a redirect uri of http://localhost:11224/AuthCallback/IndexAsync which didn't even exist in my web application's Authorized redirect uris. It should have been http://localhost:11224/MVC/AuthCallback/IndexAsync. So in the url result's redirect url I changed it to what it should have been which allowed me to complete the authorization. Now I can access the documents in the authenticated account.
var result = await new AuthorizationCodeMvcApp(this, new AppFlowMetadata()).AuthorizeAsync(cancellationToken);
I have an app running on Windows Phone 8.1 which calls a URL via InAppBrowser plugin. This URL is supposed to ask for the user certificate stored on a virtual smartcard on the phone.
When I call the URL via Internet Explorer, I am asked for my PIN to unlock the virtual smartcard but in the InAppBrowser, this doesn't work. No PIN prompt, nothing.
Iterating through the Certificates yielded from
IReadOnlyList<Certificate> certStores = await CertificateStores.FindAllAsync();
I can see the certificate at app runtime but InAppBrowser doesn't seem to query for them. Do I have to copy its reference to another certificate store or is InAppBrowser not capable of establishing SSL with user certificates ?
The issue is with the webview component, x-ms-webview to be more precisely. InAppBrowser plugin uses this component internally.
Found a workaround mentioned here, it kinda sounds like a security issue tbh so this could get fixed in the future but here are more details on said workaround:
Make a request to the URL which is supposed to trigger virtual smartcard unlock to access the user certificate, but with the HttpClient at native level (C#)
I've created another Windows Runtime Component in my solution which does a simple POST to the url I want to access from InAppBrowser later on.
While setting up the Windows.Web.Http.HttpClient, I fetch the user certificate from the smartcard and set it as HttpBaseProtocolFilter.ClientCertificate.
public sealed class SSLHelper
{
private static String errorMessage = "";
private static String statusMessage = "";
public static IAsyncOperation<Boolean> establishSSLConnection(String url)
{
return connect(url).AsAsyncOperation<Boolean>();
}
public static String getErrorMessage()
{
return SSLHelper.errorMessage;
}
public static String getStatusMessage()
{
return SSLHelper.statusMessage;
}
private static async Task<Boolean> connect(String urlString)
{
Certificate clientCert = await getCertificateAsync();
HttpBaseProtocolFilter filter = new HttpBaseProtocolFilter();
filter.ClientCertificate = clientCert;
HttpClient client = new HttpClient(filter);
try
{
System.Uri url = new System.Uri(urlString);
HttpResponseMessage response = await client.PostAsync(url, new HttpStringContent(""));
response.EnsureSuccessStatusCode();
SSLHelper.statusMessage = response.StatusCode.ToString();
return true;
}
catch (Exception e)
{
SSLHelper.errorMessage = e.ToString();
return false;
}
}
private static async Task<Certificate> getCertificateAsync()
{
CertificateQuery query = new CertificateQuery();
query.IssuerName = "Sample Issuer";
IReadOnlyList<Certificate> certStores = await CertificateStores.FindAllAsync(query);
return certStores.FirstOrDefault<Certificate>();
}
}
Make that code return as a promise on Javascript level and once it resolves, start the code which uses InAppBrowser to access the secure URL again. The native request causes the PIN prompt for virtual smartcard access, once you have entered the correct PIN, InAppBrowser / WebView can magically establish the connection.
I'm looking for a Google Drive API authentication scheme that will give a service application (on a server) rights to create shared documents in a Drive folder, without user interaction.
Google's current unique name for the specific authentication scheme I should use for this is probably a sufficient answer to this question.
Although the document creation will occur in response to a user action, the documents will not be permanently associated with those users and I do not wish to require any user to present a Google account. Instead, I wish the user to be able to access the document via an "Anyone with the link can edit"-type URL displayed on a web page after the document is created.
This is intended to automatically generate documents for multiple generally anonymous people to collaborate, and all documents will be stored in a single folder.
There's a good chance this is a duplicate of this question: Google Drive API username + password authentication. Unfortunately, the accepted answer doesn't contain enough information for me to find my way now that the links it references are dead.
It may also be a duplicate of other questions that have accepted but unclear answers, such as: .NET Google Docs API Authentication (without user interaction), How do I authenticate Google Calendar API v3 without user interaction?, and Drive API doc upload from a server without user interaction.
Authenticating as a service account was the approach I needed.
The Google SDK actions were simply misleading. When I provided some incorrect values it fell back to user-based authentication (automatically opening a web browser to request interactive credentials). I incorrectly interpreted this to mean that the service account functionality was implemented as a long-term key approved by and in the context of a specific interactive user, or something similar.
No user interaction was necessary, however the .p12 certificate was required, rather than whatever credentials the default .json file provided (which I had tried using in a number of ways). Here's the code I used:
using Google.Apis.Auth.OAuth2;
using Google.Apis.Drive.v2;
using Google.Apis.Drive.v2.Data;
using Google.Apis.Http;
using Google.Apis.Services;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
using GData = Google.Apis.Drive.v2.Data;
public class Drive
{
private const string GoogleDocMimeType = "application/vnd.google-apps.document";
/// <summary>
/// Creates a drive service, authenticated using information found in the Google Developers Console under "APIs & auth / Credentials / OAuth / Service account"
/// </summary>
/// <param name="svcEmail">The service account "Email address"</param>
/// <param name="certPath">The path to the service account "P12 key" file</param>
public Drive(string svcEmail, string certPath)
{
Service = AuthenticateService(svcEmail, certPath);
}
private DriveService Service
{
get;
set;
}
/// <summary>
/// Creates a "Google Doc" and shares it with anyone with the link
/// </summary>
/// <param name="title"></param>
/// <returns>The drive FileId, accessible at https://docs.google.com/document/d/FileId </returns>
public async Task<string> CreateShared(string title)
{
var fileId = await CreateDocument(title);
await ShareFile(fileId);
return fileId;
}
private async Task<string> CreateDocument(string title)
{
var file = new GData.File
{
Title = title,
MimeType = GoogleDocMimeType
};
file = await Service.Files.Insert(file).ExecuteAsync();
return file.Id;
}
private async Task ShareFile(string fileId)
{
Permission permission = new Permission
{
Type = "anyone",
Role = "writer",
WithLink = true
};
var a = Service.Permissions.Insert(permission, fileId);
await a.ExecuteAsync();
}
private static DriveService AuthenticateService(string svcEmail, string certPath)
{
string[] scopes = new[] { DriveService.Scope.DriveFile };
X509Certificate2 certificate = new X509Certificate2(certPath, "notasecret", X509KeyStorageFlags.Exportable);
var init = new ServiceAccountCredential.Initializer(svcEmail) { Scopes = scopes };
IConfigurableHttpClientInitializer credential = new ServiceAccountCredential(init.FromCertificate(certificate));
return new DriveService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "Document Management Service",
});
}
}
And here's an experimental consumer:
internal class Program
{
private const string svcEmail = "serviceaccountid#developer.gserviceaccount.com";
private const string certPath = #"credentials\projectname-fingerprintprefix.p12";
private readonly static Drive drive = new Drive(svcEmail, certPath);
private static void Main(string[] args)
{
string id = drive.CreateShared("my title").Result;
Console.WriteLine(id);
}
}
This seems to use Google Drive storage in an isolated, application/project-specific data repository. According to other posts, there is no way to get an interactive Drive UI view on that. I don't know if if it uses my personal storage quota, etc. But, this is the best approach I have so-far and I'll answer those questions for myself (not here) next.
I am able to authorize my application using oAuth ( I get Access token and secret in return). I use those to instantiate an object of OauthParameter. My question is as I have added scope for "google drive" during authentication. How can I create an object of DriveService, just the way PicasaService is being created.
PicasaService service;
GOAuthRequestFactory requestFactory;
string Picasa_CONSUMER_KEY = "<CONSUMER KEY>";
static string Picasa_CONSUMER_SECRET = "<CONSUMER SECRET>";
public static string Picasa_SCOPE = "http://picasaweb.google.com/data/https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/drive.file https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile";
string Picasa_CALLBACK_URL = "<CALLBACK URL>";
This is how I am creating an object of PicasaService
OAuthParameters parameters = new OAuthParameters()
{
ConsumerKey = Picasa_CONSUMER_KEY,
ConsumerSecret = Picasa_CONSUMER_SECRET,
Token = "token received during authentication",
TokenSecret = "secret received during authentication"
};
requestFactory = new GOAuthRequestFactory("XYZ", "XYZ APP", parameters);
service = new PicasaService(requestFactory.ApplicationName);
service.RequestFactory = requestFactory;
Now how can I "parameters" to instantiate an object of DriveService??
As I already have the scope of Google Drive, and received token, secret during the authentication process, I don't want to do oAuth separately for google drive.
Thanks
Sujit