Skip to content
This repository has been archived by the owner on Dec 13, 2018. It is now read-only.

JwtBearer and Facebook Authentication #1439

Closed
no1melman opened this issue Sep 18, 2017 · 13 comments
Closed

JwtBearer and Facebook Authentication #1439

no1melman opened this issue Sep 18, 2017 · 13 comments

Comments

@no1melman
Copy link

I've got both these setup:

services.AddAuthentication(options =>
                {
                    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
                    
                })
                .AddJwtBearer(bearerOptions =>
                {
                    bearerOptions.TokenValidationParameters = TokenParameters(Environment);
                    bearerOptions.Events = new JwtBearerEvents();
                })
                .AddFacebook(facebookOptions =>
                {
                    facebookOptions.AppId = Configuration["Authentication:Facebook:AppId"];
                    facebookOptions.AppSecret = Configuration["Authentication:Facebook:AppSecret"];
                    facebookOptions.Scope.Add("email");
                    facebookOptions.Fields.Add("name");
                    facebookOptions.Fields.Add("email");
                    facebookOptions.Events = new OAuthEvents
                    {
                        OnCreatingTicket = context =>
                        {
                            return Task.CompletedTask;
                        },
                        OnRemoteFailure = context =>
                        {
                            return Task.CompletedTask;
                        }
                    };
                });

I know I have set defaults, but I'm guessing that only intervenes when it can't understand the Authorization header.

So my JavaScript does this:

    facebookLogin() {
        const { history } = this.props;

        window.FB.login(loginResponse => {
            debugger;

            fetch('/api/values', {
                method: 'POST',
                headers: { 
                    'Authorization': `Facebook ${loginResponse.authResponse.accessToken}`
                }
            })
                .then(res => {
                    if (res.status === 401) {
                        history.push('/login');
                        return null;
                    }
        
                    console.log(res.text());
                });
        })
    }

So, it should be pushing the Authorization=Facebook {access_token} to the backend, and I expect it to then use the FacebookAuthenticationHandler to do authentication.

A) I can't understand from your source code where the auth scheme is pulled out of the request.

B) The log output is saying it is trying to do Bearer authentication (which I guess is doing this because I've set the default to be that)

So how do I get them playing nice together with this setup (and where on earth is the auth scheme being pulled out)

debug

Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler:Information: AuthenticationScheme: Bearer was challenged.

@HaoK
Copy link
Member

HaoK commented Sep 18, 2017

[Authorize] uses the DefaultChallengeScheme when you don't specify a scheme in the attribute. There's no way for a remote auth scheme like Facebook to sign in via a bearer token, only cookies supports sign in of the built in auth schemes

@no1melman
Copy link
Author

I'm not trying to sign in via Facebook, I've already done that using Fb.login(), I'm just trying to get the Facebook OAuth Handler to verify that the token it has received is legit.... and then get the creds from the Facebook Api. If that isn't the proper flow - how do you get facebook to accept a callback url that is the Facebook Options Callback?

@HaoK
Copy link
Member

HaoK commented Sep 18, 2017

I don't believe the OAuth handlers are designed to be able to validate tokens that you got yourself in this way, @Tratcher is there any way he can reuse anything to accomplish this?

@Tratcher
Copy link
Member

No, our oauth providers don't work like that. They only support the fully interactive redirect flow. Once you have retrieved the token in javascript you can use it directly to get the user information.

@no1melman
Copy link
Author

Is there proper documentation on getting this flow to work?!

@Tratcher
Copy link
Member

The JS flow is unrelated to the server, you just use the Facebook JS SDK in client code.

@no1melman
Copy link
Author

Thats the part I'm having trouble with, I'm using the FacebookJs SDK, but that isn't invoking the oauth callback.

@Tratcher
Copy link
Member

Which callback would that be? That should all be local to the client, e.g. in a pop up or IFrame, it shouldn't be invoking a callback on your server.

@no1melman
Copy link
Author

no1melman commented Sep 18, 2017

It would appear that the external logins only seem to work with MVC, not with a SPA. With MVC there is a flow to do ChallengeAsync where you pass in the provider (a.k.a Facebook) - and I'm guessing from there the provider does all the work with redirecting etc.

So I'm guessing that in order to use the FB JS SDK i would have to write my own middleware that used the FB access token endpoint to authorise a token sent from the client.

Otherwise I just have a button that invokes a middleware to do a ChanllengeAsync and pass in the provider - which seems a bit disjointed to me.

@Tratcher
Copy link
Member

Why a middleware vs a Controller Action?

@no1melman
Copy link
Author

Because I'm working in the SPA world, so my ASPNET app is just a REST service. Secondly I don't like using the attributes, I prefer the middleware approach where the path of my requests and responses are very prescribed. I don't see why this offering can't be in middleware and why it has only been build for the MVC world, but there you go

@Tratcher
Copy link
Member

Tratcher commented Oct 6, 2017

Note that the access_token from facebook does not contain anything that you can validate on your server. Your only way to validate it is to send it to a facebook REST API and see if it succeeds. For instance you could send it to the user information endpoint. This is like skipping to the last step in the existing facebook handler.

var endpoint = QueryHelpers.AddQueryString(Options.UserInformationEndpoint, "access_token", tokens.AccessToken);
if (Options.SendAppSecretProof)
{
endpoint = QueryHelpers.AddQueryString(endpoint, "appsecret_proof", GenerateAppSecretProof(tokens.AccessToken));
}
if (Options.Fields.Count > 0)
{
endpoint = QueryHelpers.AddQueryString(endpoint, "fields", string.Join(",", Options.Fields));
}
var response = await Backchannel.GetAsync(endpoint, Context.RequestAborted);
if (!response.IsSuccessStatusCode)
{
throw new HttpRequestException($"An error occurred when retrieving Facebook user information ({response.StatusCode}). Please check if the authentication information is correct and the corresponding Facebook Graph API is enabled.");
}
var payload = JObject.Parse(await response.Content.ReadAsStringAsync());
var context = new OAuthCreatingTicketContext(new ClaimsPrincipal(identity), properties, Context, Scheme, Options, Backchannel, tokens, payload);
context.RunClaimActions();

@Tratcher Tratcher removed their assignment Oct 6, 2017
@Tratcher Tratcher added this to the Discussion milestone Oct 6, 2017
@aspnet-hello
Copy link

We periodically close 'discussion' issues that have not been updated in a long period of time.

We apologize if this causes any inconvenience. We ask that if you are still encountering an issue, please log a new issue with updated information and we will investigate.

@aspnet-hello aspnet-hello removed this from the Discussion milestone Jan 27, 2018
@Eilon Eilon added this to the Discussion milestone Jan 27, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

5 participants