1

Edit:

Here is my question reformulated:

I have a web server with secured api endpoints - one must have been authenticated with Google prior to using them. I implemented Challenge and Callback endpoints for that.

This works well from a browser with my SPA web front-end. The user gets redirected to the Google website to sign-in and then gets redirected back to my webapp; the browser then has the authenticated cookies and the webapp can use the endpoints to update its state.

I also have a WPF application that will communicate with the web server. I want the WPF application to do the same as the web front-end: Use the web api endpoints after being authenticated with Google. The connection between the WPF application and my web server is done through an HttpClient.

My problem is I don't know how to authenticate that HttpClient connection between the WPF app and the web server.

I tried using the same Challenge endpoint but the response I get is of course the HTML from the Google Sign-In page, so I guess I can't use that with an HttpClient...

I also tried authenticating with GoogleApis from the WPF app and use the authenticated token to set cookies in the HttpClient but apparently this is not compatible.

How to authenticate an HttpClient connection to a web api with an external provider such as Google?


Original question:

From a WPF application, the user authenticates with Google with this code:

using Google.Apis.Auth.OAuth2;
...
public void Authenticate()
{
    UserCredential credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
        new ClientSecrets
        {
            ClientId = "myClientId",
            ClientSecret = "myClientSecret"
        },
        new[] { "email", "openid" },
        "user",
        CancellationToken.None).Result;
}

This works and the UserCredential object contains the authenticated token:

enter image description here

How to embed this token information in a web request made with an HttpClient in order to call my webapi endpoint?

I think the request must include some cookies to inform the server that it has been authenticated, but I don't know which ones exactly.

The endpoint on the server-side validates that the user is authenticated with the help of IdentityServer:

var result = await HttpContext.AuthenticateAsync(IdentityServer4.IdentityServerConstants.ExternalCookieAuthenticationScheme);
if (result?.Succeeded != true)
{
    throw new Exception("External authentication error");
}
Bruno
  • 4,685
  • 7
  • 54
  • 105
  • https://developers.google.com/api-client-library/dotnet/guide/aaa_oauth – Andrei Dragotoniu Oct 23 '19 at 06:56
  • How does your WebAPI validate the token? – user1672994 Oct 25 '19 at 08:25
  • Hi @user1672994, from my limited understanding of OpenId, that line in the Callback endpoint is doing it magically with IdentityServer4: `var result = await HttpContext.AuthenticateAsync(IdentityServer4.IdentityServerConstants.ExternalCookieAuthenticationScheme);`. I can post the full code of the Challenge and Callback endpoints if required. – Bruno Oct 25 '19 at 14:19
  • Web API should be working on Bearer token instead of challenge. You can check how to setup Authentication for WebApi with IdentityServer using Google provider – user1672994 Oct 25 '19 at 14:33
  • @user1672994 Thanks, I would need some help with that yes – Bruno Oct 25 '19 at 14:34

4 Answers4

2

If I got your question right, you just have to set the Authorization header

var credentials = await GoogleWebAuthorizationBroker.AuthorizeAsync(
        clientSecrets,
        new[] { "email", "openid" },
        "user",
        CancellationToken.None);

_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
        credentials.Token.TokenType, 
        credentials.Token.IdToken);
Leff
  • 582
  • 3
  • 12
0

Maybe you'll find below a helpful hint to better understand OpenID :)

The confusion stems from mixing GoogleApis and IdentityServer frameworks. Authentication/authorization can be achieved using either of them. Objects from Google.Apis.Auth.OAuth2 and IdentityServer4 namespaces are not designed to interact. No manual cookie handling is necessary, for sure.

Ask yourself to whom does Google provide trust for the user. If it calls back to WPF, then webapi trusting WPF is a separate issue.

  • Thanks for your kind reply. If you can help me further, here is the scenario : A wpf application requires to communicate with my web api. The web api need the requests to be authenticated with Google first. – Bruno Oct 27 '19 at 16:11
  • This issue got me spinning, but I see a theoretical solution: - WPF initiates auth as demonstrated in your first code snippet - Google calls-back both WPF and webapi. Multiple redirect uris may be provided in the Google service configuration. Now both apps are aware of the authenticated user. I'll try to set that in code tomorrow. – Victor Popescu Oct 27 '19 at 17:26
  • This is interesting. I am not sure if the web api backend will consider that the connection from the WPF application is authenticated even if it receives an authentication from another channel? For example: if I authenticate a connection to my server by using a browser, and I open a second browser in incognito, that new session is not authenticated even though the first one is. I was looking for a way to authenticate the `HttpClient` connection between the WPF application and the web server, but really I don't know anymore how to do this O_o – Bruno Oct 27 '19 at 18:04
  • WPF is a front-end. It could be Angular. WebApi the backend. This is a fairly common scenario. There must be a tried and true solution. I'll look it up on a fresh head :) – Victor Popescu Oct 27 '19 at 20:30
  • Well with a web front-end, it is working well (see my edit) since the browsar can handle both the cookies and the redirection. The problem with a HttpClient connection is that I don't see a way to manage the Google authentication screen through it...Thanks for looking at this =) – Bruno Oct 27 '19 at 21:30
  • To authenticate a web api the best is to use the Bearer Token. If you want to use the google OAuth then you will have to show a page where google can ask credentials and user has to manually authenticate himself before your application actually fires the API. Just a general advise use Bearer Token – Prakash Oct 30 '19 at 09:17
  • Not too familiar with WPF; where does the google auth page go in the app? – Victor Popescu Oct 30 '19 at 10:59
0

You answer your own question in the question:

the browser then has the authenticated cookies and the webapp can use the endpoints to update its state

HttpClient needs to send those same cookies. How do I set a cookie on HttpClient's HttpRequestMessage

Rhyous
  • 6,510
  • 2
  • 44
  • 50
0

If I understood your question right, then I faced the same problem not too long ago.

The way I implemented it is that in the backend, no matter who tries to access the endpoint, they had to send a Bearer X authorization token. The token contained the identity of the client that wanted to access the resource, and I checked if he was permitted.

No matter what kind of client wants to access the endpoint, it just has to have that authroziation header in the request that he sends and the backend will treat it the same.

In my scenario, I used an authentication service that returns a cookie to the client with a certain JWT that contains the identity information. Then from the client I send in every request the JWT received from the authentication service as an authorization header to the backend.

The reason I had to put the JWT that I receive from the service in a header, is that the authentication service and the backend service are not in the same domain, so cookies cant be shared.

This results in such design that no matter how you authenticate the client, the end result must be some sort of token that the backend can receive and read.

Hope this helps.

Max
  • 907
  • 2
  • 13
  • 27