简体   繁体   中英

How to authenticate an HttpClient connection with an external provider (Google)

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:

在此处输入图像描述

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");
}

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);

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.

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

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM