SPA - Firebase 和 .Net WebApi 2 身份验证 [英] SPA - Firebase and .Net WebApi 2 authentication

查看:33
本文介绍了SPA - Firebase 和 .Net WebApi 2 身份验证的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个用 AngularJs 编写的单页应用程序(此时框架无关紧要)该应用程序托管在 IIS 中,它由 index.html 和一堆客户端资产组成.

在后端,我有 WebApi 2,它也作为单独的应用程序托管在 IIS 中.

对于客户端身份验证,我使用 Firebase(简单登录)并启用了多个社交网络,例如 Facebook、Twitter 或 Google.

到目前为止一切顺利.我喜欢使用 firebase 启用 twitter 身份验证是多么容易.

在使用社交网络登录时,我从 firebase、firebaseAuthToken 和提供者访问令牌中返回.

现在我想使用 firebaseAuthToken 或提供者访问令牌来验证我的 WebApi.

问题是:在给定条件下使用 WebApi 进行身份验证的最佳方法是什么?

没有选择只使用 firebase 来存储我的数据并摆脱 web api,因为我在服务器上设置了复杂的业务逻辑.

到目前为止,我的一个愚蠢想法是将社交提供商访问令牌传递给服务器,根据提供商验证令牌,然后使用 Owin -Katana 颁发安全令牌.

由于缺乏文档、复杂性以及与单页应用程序的不良集成,我没有使用 katana 的内置社交提供商支持.我发现 SPA 的 Visual Studio 模板太特定于 mvc.但那是我 :)

解决方案

tl;dr - )解码 JWT).如果解码成功,那么我们可以检查令牌以确保它没有过期.

 公共类 DecodeJWT: ActionFilterAttribute{公共覆盖无效 OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext){string firebaseAuthToken = string.Empty;如果(actionContext.Request.Headers.Authorization != null){firebaseAuthToken = actionContext.Request.Headers.Authorization.Scheme;} 别的 {throw new HttpException((int) HttpStatusCode.Unauthorized, "Unauthorized");}string secretKey = WebConfigurationManager.AppSettings[FirebaseSecret"];尝试 {字符串 jsonPayload = JWT.JsonWebToken.Decode(firebaseAuthToken, secretKey);DecodedToken decodedToken = JsonConvert.DeserializeObject <解码令牌>(jsonPayload);//TODO: 检查解码令牌的到期时间} catch (JWT.SignatureVerificationException jwtEx) {throw new HttpException((int) HttpStatusCode.Unauthorized, "Unauthorized");} 捕捉(异常前){throw new HttpException((int) HttpStatusCode.Unauthorized, "Unauthorized");}base.OnActionExecuting(actionContext);}}

创建一个 $httpInterceptor 将 firebaseAuthToken 添加到每个请求的标头

在客户端,诀窍是每次都必须传递令牌.为了使这更容易,我们需要使用 Angular 创建一个 $httpInterceptor 来检查 sessionStorage 上的 firebaseAuthToken.

.factory('authInterceptor', function ($rootScope, $q, $window) {返回 {请求:功能(配置){config.headers = config.headers ||{};如果($window.sessionStorage.firebaseAuthToken){config.headers.Authorization = $window.sessionStorage.firebaseAuthToken;}返回配置;},响应:函数(响应){如果(响应.状态 === 401){//TODO: 用户未经过身份验证}回复回复 ||$q.when(响应);}};})

成功登录时将 firebaseAuthToken 设置为 sessionStorage

每当用户登录时,我们都可以将该值设置为 sessionStorage.

$rootScope.$on('$firebaseSimpleLogin:login',功能(e,用户){//为身份验证令牌添加一个 cookie如果(用户){$window.sessionStorage.firebaseAuthToken = user.firebaseAuthToken;}cb(e, 用户);});

全局注册DecodeJWT过滤器

WebApiConfig.cs 注册方法内部,我们可以设置 DecodeJWT 过滤器以应用到我们所有的 ApiController.

config.Filters.Add(new DecodeJWT());

现在每当我们向 ApiController 发出请求时,它都会拒绝它,除非有一个有效的 JWT.因此,在用户登录后,如果 ApiController 不存在,我们可以将其数据保存到 ApiController.

//全局使用 DecodeJWT公共类用户控制器:ApiController{//POST api/用户public void Post([FromBody] FbUser user)//此模型见 GitHub{//如果我们还没有用户,则保存它}}

I'm having a Single Page Application written in AngularJs (The framework is irrelevant at this point) The application is hosted in IIS and it's compose of index.html plus a bunch of client assets.

On backend I have WebApi 2, hosted also in IIS as a separate application.

For authentication on client I'm using Firebase (simple login) with several social netoworks enabled, like Facebook, Twitter or Google.

So far so good. I like how easy it is to enable twitter authentication for example with firebase.

On login with social network i get back from firebase, the firebaseAuthToken and provider accesstoken.

Now I want to use firebaseAuthToken or provider access token to authenticate with my WebApi.

The question is: What is the best way to authenticate with WebApi in given conditions?

There is not an option to use only firebase to store my data and get rid of web api since I have in place complex business logic on server.

One silly idea that i have so far, is to pass the social provider access token to the server, validate the token against provider and then issue a security token using Owin -Katana.

I'm not using build in social providers support from katana due to lack of documentation, complexity and bad integration with single page apps. I found the visual studio template for SPA too mvc specific. But that's me :)

解决方案

tl;dr - Demo Project on GitHub

The steps below may seem long, but it's actually really easy. I created my demo project in just an hour or so.


I agree with you about using Owin and Katana. I've been through that process before and it wasn't really a great experience. Using Firebase was a heck of a lot easier.

This can all be done with JWTs!

When you authenticate through Firebase and whatever social provider, you get back a JSON Web Token (JWT) - firebaseAuthToken.

Grab your Firebase Secret from the Dashboard

The way JWTs work is that we have a secret token and a client token. The client token is the firebaseAuthToken we receive after logging in. The secret token is generated for us in the Firebase Dashboard.

Store your Firebase Secret in the appSettings section of your Web.config

We need to store this secret key in the Web.config so it's easier to access later.

<add key="FirebaseSecret" value="<Firebase-Secret-Token-Goes-Here" />

Create an Action Filter to check the JWT from the Authorization Header

We can verify the request is valid by passing the client token in the Authorization header. On the server we can store our secret key that we get from our Firebase Dashboard. When the request is checked by Web API we can decode the JWT using a JWT Library (available from NuGet). If the decoding is successful then we can check to token to make sure it isn't expired.

public class DecodeJWT: ActionFilterAttribute 
{

    public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext) 
    {
        string firebaseAuthToken = string.Empty;
        if (actionContext.Request.Headers.Authorization != null) {
            firebaseAuthToken = actionContext.Request.Headers.Authorization.Scheme;
        } else {
            throw new HttpException((int) HttpStatusCode.Unauthorized, "Unauthorized");
        }

        string secretKey = WebConfigurationManager.AppSettings["FirebaseSecret"];
        try {
            string jsonPayload = JWT.JsonWebToken.Decode(firebaseAuthToken, secretKey);
            DecodedToken decodedToken = JsonConvert.DeserializeObject < DecodedToken > (jsonPayload);
            // TODO: Check expiry of decoded token
        } catch (JWT.SignatureVerificationException jwtEx) {
            throw new HttpException((int) HttpStatusCode.Unauthorized, "Unauthorized");
        } catch (Exception ex) {
            throw new HttpException((int) HttpStatusCode.Unauthorized, "Unauthorized");
        }

        base.OnActionExecuting(actionContext);
    }

}

Create a $httpInterceptor add the firebaseAuthToken to the header for every request

On the client, the trick is that the token has to be passed every time. To make this easier we need to create a $httpInterceptor with Angular that checks for a firebaseAuthToken on sessionStorage.

.factory('authInterceptor', function ($rootScope, $q, $window) {
    return {
        request: function (config) {
            config.headers = config.headers || {};
            if ($window.sessionStorage.firebaseAuthToken) {
                config.headers.Authorization = $window.sessionStorage.firebaseAuthToken;
            }
            return config;
        },
        response: function (response) {
            if (response.status === 401) {
                // TODO: User is not authed
            }
            return response || $q.when(response);
        }
    };
})

Set the firebaseAuthToken to sessionStorage on a successful login

Whenever a user logs in we can set the value to sessionStorage.

$rootScope.$on('$firebaseSimpleLogin:login',
    function (e, user) {

        // add a cookie for the auth token
        if (user) {
            $window.sessionStorage.firebaseAuthToken = user.firebaseAuthToken;
        }

        cb(e, user);
    });

Register the DecodeJWT filter globally

Inside of the WebApiConfig.cs Register method we can set the DecodeJWT filter to apply for all of our ApiControllers.

config.Filters.Add(new DecodeJWT());

Now whenever we make a request to an ApiController it will reject it unless there is a valid JWT. So after a user logs in we can save their data to a ApiController if it already doesn't exist.

// globally uses DecodeJWT
public class UsersController: ApiController 
{
    // POST api/users
    public void Post([FromBody] FbUser user) // See GitHub for this Model
    {
        // Save user if we do not already have it
    }
}

这篇关于SPA - Firebase 和 .Net WebApi 2 身份验证的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆