Generating JWT token from facebook auth

Hi,

I am having a little bit of trouble figuring out OAuth authentication.

This is my current auth provider setup:

Plugins.Add(new AuthFeature(() => new AuthUserSession(),
	new IAuthProvider[] {
	new JwtAuthProvider(AppSettings) { AuthKeyBase64 = "cGFydHkgdGltZQ==", RequireSecureConnection=false },
	new CredentialsAuthProvider(AppSettings),
	new FacebookAuthProvider(AppSettings) {
		RedirectUrl = "http://localhost:4200/pages/auth/register",
		CallbackUrl = "http://localhost:5100/auth/facebook",
		AppId = "123",
		AppSecret = "123",
		Fields = new string[] { "id", "email", "first_name", "last_name" },
		Permissions = new string[] {"email"}
	},
	}));

When I hit http://localhost:5100/auth/facebook the first time I approve permissions then it re-directs me back to site. I can see in database a user has been created. The page I get returned to is http://localhost:4200/pages/auth/register?s=1#_=_ and I can see ss-id, ss-opt and ss-pid cookies are created on the application screen of chrome dev tools.

This is where I am getting confused. My current way to handle auth on frontend is to create a local object that stores the JWT token and then reference this when making requests. If I get an unauthorized error I delete the object and direct user to login page. I plan to use the role meta data in the JWT to customize the front end options.

How does this fit in with the OAuth cookies? From reading the documentation it seems like I have to now convert the session cookies into a token but I am struggling to figure that out.

I dont seem to be able to access the cookies from javascript. When I console.log(document.cookie) the ss-id and ss-pid are not in the returned string but I can see them in dev tools.

I assume I need to do something along the lines of this in front end:

var client = new JsonServiceClient("http://localhost:5100");
var ssid : Cookie = {
	name: "ss-id",
	path: "/",
	value: this.cookieService.get('ss-id')
}
var sspid : Cookie = {
	name: "ss-pid",
	path: "/",
	value: this.cookieService.get('ss-pid')
}
client.cookies[0] = ssid;
client.cookies[1] = sspid;

var tokenResponse = await client.post(new ConvertSessionToToken());

And then I get the token? I can’t find any example of this so I feel like I am fumbling in the dark and I am unable to read the cookies generated by facebook auth so I am not sure if I am going in right direction or not.

What should I be doing after user is re-directed back from facebook to generate a token?

This is documented in the docs under Switching existing Sites to JWT where it provides links to working example apps that show how they convert their OAuth Authenticated UserSession into a JWT.

Essentially after the redirect back to the web page you need to make a call to ConvertSessionToToken API using any of the examples in the docs. This returns the UserSession in a JWT Cookie that will be sent on any future API requests to enable Authenticated requests.

I never used Angualr 1 but this example looks like it gets session from /my-session then posts it to /session-to-token

$http.get('/my-session').success(function (response) {
	$rootScope.currentUserSession = response;
	$rootScope.isAuthenticated = true;
	$http.post("/session-to-token");
	deferred.resolve(response);
})
.error(function (error) {
	$rootScope.currentUserSession = null;
	$rootScope.isAuthenticated = false;
	deferred.reject(error);
});

So I tried:

var client = new JsonServiceClient("http://localhost:5100");
var session = await client.get(new Authenticate());
var token = await client.post(new ConvertSessionToToken(), session);
console.log(session);
console.log(token);

It successfully gets the session but the token response is always empty. I have tried setting preserveSession to true and playing around with syntax but no luck.

The docs say:

You can convert your Session into a Token and set the ss-tok Cookie in your web page by sending an Ajax request to /session-to-token, e.g:

$.post(“/session-to-token”);

I get the ss-tok cookie when I send the empty authenticate. How do I use it to make the token. I tried variations of:

var response = this.http.post("http://localhost:5100/session-to-token", {})
  .subscribe(data => {
    console.log(data);
  });

I have read the documentation several times and I cant make sense of it. How do I get the token response with the typescript client? I need to store the bearer token and the roles in local object.

The docs say

If authenticated, sending an empty Authenticate() DTO will return the currently Authenticated User Info that also generates a new JWT Token from the User’s Authenticated Session and returns it in the BearerToken Response DTO property which we can use to update our invalidated JWT Token.

But it doesn’t return the bearer token like credentials authentication does. The bearer token and refresh token are missing from the returned object.

Why is it not generating token with facebook authentication?

As far as I can see /my-session and /session-to-token don’t actually return anything. Am I missing a plugin they require?

var client = new JsonServiceClient("http://localhost:5100");
    await client.post(new ConvertSessionToToken(), session);

This sets the ss-tok cookie. How do I get the bearer token and roles from the ss-tok cookie?

As a I mentioned above, ConvertSessionToToken returns a JWT Cookie.

Why do you need access to the JWT in JavaScript? The recommended way for using JWT Tokens in Web Applications is using a HTTP only Cookie precisely so it’s not accessible from JavaScript where any XSS attack will give them access to an authenticated UserSession, the Cookie returned has the same expiry as the JWT Cookie so the authenticated Web App will be able to make Authenticated Requests until the JWT and Cookie expires.

If you absolutely need access to the JWT Cookie in JavaScript you can opt-in to configure your JwtAuthProvider with:

new JwtAuthProvider { 
    IncludeJwtInConvertSessionToTokenResponse = true,
}

to also include the JWT in AccessToken property of ConvertSessionToTokenResponse Responses which are otherwise only available in the ss-tok Cookie.

If the client just needs some data from JWT UserSession instead of the entire JWT Token itself, you can just call your own Service which returns the data they need.

The code in template I am re-using does it like that. It reads the bearer and meta data from token which is then referenced from services and effects the views and menus based on what is in token so I was just trying to make what was there work. I hadn’t thought about the XSS issues. Using browser cookies makes more sense. I will re-write it so it hits an endpoint to get the meta data instead.

Thanks for the advice Mythz, really appreciate it.

Sorry to bombard you with questions but I have another…

I am trying to see if I can add in additional fields to the AuthResponse.

I built the custom provider class:

    public CustomFacebookAuthProvider(IAppSettings appSettings) : base(appSettings)
    {

    }
    public void Execute(AuthFilterContext authContext)
    {
        authContext.AuthResponse.DisplayName = authContext.Session.FirstName;
    }

Which lets me change stuff but I want to extend the response.

The doc says:

to completely replace what AuthenticateResponse is returned, specify a AuthFeature.AuthResponseDecorator

I am not sure what this means. I can see the AuthFeature class has property:

public Func<AuthFilterContext, object> AuthResponseDecorator { get; set; }

But I am not sure how it is meant to be used. Can the auth response be extended with some more properties?

ServiceStack built-in DTO Types cannot be altered, the recommended way to return additional info in any of ServiceStack built-in DTOs is to return them in the Meta dictionary. Alternatively you could add Custom HTTP Headers but that wouldn’t be as accessible as returning them in the AuthenticateResponse DTO.

Note: you can’t use the AuthDecorator if using JWT AuthProvider as there can only 1 AuthDecorator configured which is already used by the JwtAuthProvider to return the AuthenticateResponse DTO in a custom HttpResult.

Instead you can add modify the AuthenticateResponse with a Response Filter:

this.GlobalResponseFilters.Add((req, res, responseDto) => 
{
    if (responseDto as AuthenticateResponse authResponse)
    {
        authResponse.Meta = new Dictionary<string,string> { 
            {"MyCustomProp", customPropValue}
        };
    }
});