ASP.NET OAuth授权 - 使用客户端Id和秘密及用户名和密码的区别 [英] ASP.NET OAuth Authorization - Difference between using ClientId and Secret and Username and Password

查看:181
本文介绍了ASP.NET OAuth授权 - 使用客户端Id和秘密及用户名和密码的区别的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想在ASP.NET的WebAPI 2.我的主要目的是实现一个简单的 OAuthAuthorizationServerProvider 是要学会如何对移动应用的令牌。我想用户提供用户名和放大器来登录;密码,然后领取一个令牌(和刷新令牌,使他们不必重新输入一次凭据,令牌到期)。后来,我想必须打开其他应用程序外部使用API​​的机会(比如人使用Facebook的API和等...)。

下面是我是如何建立我的AuthorizationServer:

  app.UseOAuthAuthorizationServer(新OAuthAuthorizationServerOptions()
{
    AllowInsecureHttp = TRUE,
    TokenEndpointPath =新PathString(/标记),
    AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(5),
    供应商=新SimpleAuthorizationServerProvider(新SimpleAuthorizationServerProviderOptions()
    {
        ValidateUserCredentialsFunction =的ValidateUser
    }),
    RefreshTokenProvider =新SimpleRefreshTokenProvider()
});

这是我的 SimpleAuthorizationServerProviderOptions 实施

 公共类SimpleAuthorizationServerProvider:OAuthAuthorizationServerProvider
{
    公共委托任务<布尔> ClientCredentialsValidationFunction(ClientID的字符串,字符串密码);
    公共委托任务<&IEnumerable的LT;权利要求GT;> UserCredentialValidationFunction(用户名字符串,字符串密码);
    公共SimpleAuthorizationServerProviderOptions选项{搞定;私人集; }    公共SimpleAuthorizationServerProvider(SimpleAuthorizationServerProviderOptions选项)
    {
        如果(options.ValidateUserCredentialsFunction == NULL)
        {
            抛出新的NullReferenceException(ValidateUserCredentialsFunction不能为空);
        }
        选项​​=选择;
    }    公共SimpleAuthorizationServerProvider(UserCredentialValidationFunction userCredentialValidationFunction)
    {
        选项​​=新SimpleAuthorizationServerProviderOptions()
        {
            ValidateUserCredentialsFunction = userCredentialValidationFunction
        };
    }    公共SimpleAuthorizationServerProvider(UserCredentialValidationFunction userCredentialValidationFunction,ClientCredentialsValidationFunction clientCredentialsValidationFunction)
    {
        选项​​=新SimpleAuthorizationServerProviderOptions()
        {
            ValidateUserCredentialsFunction = userCredentialValidationFunction,
            ValidateClientCredentialsFunction = clientCredentialsValidationFunction
        };
    }    公共覆盖异步任务ValidateClientAuthentication(OAuthValidateClientAuthenticationContext上下文)
    {
        如果(Options.ValidateClientCredentialsFunction!= NULL)
        {
            字符串的clientId,clientSecret;            如果(!context.TryGetBasicCredentials(出客户端ID,出clientSecret))
            {
                context.TryGetFormCredentials(出客户端ID,出clientSecret);
            }            VAR clientValidated =等待Options.ValidateClientCredentialsFunction(客户端ID,clientSecret);
            如果(!clientValidated)
            {
                context.Rejected();
                返回;
            }
        }        context.Validated();
    }    公共覆盖异步任务GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext上下文)
    {
        如果(Options.ValidateUserCredentialsFunction == NULL)
        {
            抛出新的NullReferenceException(ValidateUserCredentialsFunction不能为空);
        }        VAR索赔=等待Options.ValidateUserCredentialsFunction(context.UserName,context.Password);
        如果(索赔== NULL)
        {
            context.Rejected();
            返回;
        }        //创建身份
        VAR身份=新ClaimsIdentity(索赔,context.Options.AuthenticationType);        //创建元数据传递给刷新令牌提供商
        VAR道具=新AuthenticationProperties(新字典<字符串,字符串>()
        {
            {为:CLIENT_ID,context.UserName}
        });        VAR票=新AuthenticationTicket(身份,道具);
        context.Validated(票);
    }    公共覆盖异步任务GrantRefreshToken(OAuthGrantRefreshTokenContext上下文)
    {
        VAR originalClient = context.Ticket.Properties.Dictionary [为:CLIENT_ID];
        VAR currentClient = context.ClientId;        //强制客户端刷新令牌绑定
        如果(originalClient!= currentClient)
        {
            context.Rejected();
            返回;
        }        //机会改变了刷新令牌请求身份验证票证
        VAR newIdentity =新ClaimsIdentity(context.Ticket.Identity);
        newIdentity.AddClaim(新索赔(newClaim,refreshToken));        VAR newTicket =新AuthenticationTicket(newIdentity,context.Ticket.Properties);
        context.Validated(newTicket);
    }
}

和我的 SimpleRefreshTokenProvider 实施

 公共类SimpleRefreshTokenProvider:IAuthenticationTokenProvider
{
    私有静态ConcurrentDictionary<字符串,AuthenticationTicket> _refreshTokens =
        新ConcurrentDictionary<字符串,AuthenticationTicket>();    公共无效创建(AuthenticationTokenCreateContext上下文)
    {    }    公共异步任务CreateAsync(AuthenticationTokenCreateContext上下文)
    {
        VAR GUID = Guid.NewGuid()的ToString()。        VAR refreshTokenProperties =新AuthenticationProperties(context.Ticket.Properties.Dictionary)
        {
            IssuedUtc = context.Ticket.Properties.IssuedUtc,
            ExpiresUtc = DateTime.UtcNow.AddYears(1)
        };
        VAR refreshTokenTicket =新AuthenticationTicket(context.Ticket.Identity,refreshTokenProperties);        _refreshTokens.TryAdd(GUID,refreshTokenTicket);
        context.SetToken(GUID);
    }    公共无效接收(AuthenticationTokenReceiveContext上下文)
    {    }    公共异步任务ReceiveAsync(AuthenticationTokenReceiveContext上下文)
    {
        AuthenticationTicket票;
        如果(_refreshTokens.TryRemove(context.Token,出票))
        {
            context.SetTicket(票);
        }
    }
}

我不完全理解就是使用客户端Id和揭秘的VS 用户名和密码。在code我粘贴通过用户名和密码生成令牌,我可以用该令牌工作(直到它过期),但是当我试图得到一个刷新令牌,我必须有ClientID的。

另外,如果一个标记过期,正确的方法是发送刷新令牌,并得到一个新的令牌?如果刷新令牌被盗?是不是同一个用户名和放大器;密码被盗取?


解决方案

  

我不完全理解就是使用客户端Id和揭秘的VS 用户名和密码。在code我粘贴通过用户名和密码生成令牌,我可以用该令牌工作(直到它过期),但是当我试图得到一个刷新令牌,我必须有ClientID的。


  
  

另外,如果一个标记过期,正确的方法是发送刷新令牌,并得到一个新的令牌?如果刷新令牌被盗?是不是同一个用户名和放大器;密码被盗取?


在的OAuth2是必不可少的两个用户,并在由协议定义的任何授权流程客户端进行认证。客户端身份验证(因为你可能猜)只能由知名客户强制使用您的API。序列化的访问令牌,一旦产生,不绑定,直接一个特定的客户端。请注意, ClientSecret 必须被视为一机密信息,并且可以通过能够存储在某些安全的方式本信息(例如,外部服务的客户端,而不是客户端仅使用JavaScript的客户端)。

刷新令牌只是一个另类交付式为的OAuth2,正如你正确地指出,将取代用户名和口令对用户。此令牌必须被视为机密数据(甚至比访问令牌更保密的),但给出了存储用户名和放大器的优势;在客户端上的密码:


  • 它可以由用户被撤销,如果损害;

  • 它有一个有限的寿命(通常数天或数周);

  • 它不公开用户凭证(攻击者只能获得访问令牌发出刷新令牌范围)。

我建议你阅读更多关于的OAuth 2中定义的不同类型的补助在检查官方草案。我还建议你<一个href=\"http://bitoftech.net/2014/07/16/enable-oauth-refresh-tokens-angularjs-app-using-asp-net-web-api-2-owin/\"相对=nofollow>这个资源我发现非常有用时的Web API首先实现了OAuth2自己。

样品索取

下面是使用招,为资源所有者密码凭据两种请求例子格兰特

提琴手请求:资源所有者授予

刷新令牌格兰特

在这里输入的形象描述

I'm trying to implement a simple OAuthAuthorizationServerProvider in ASP.NET WebAPI 2. My main purpose is to learn how to have a token for a mobile app. I would like users to login with username & password, and then receive a token (and a refresh token so they won't have to re-enter credentials once token expires). Later on, I would like to have the chance to open the API for external use by other applications (like one uses Facebook api and such...).

Here is how I've set-up my AuthorizationServer:

app.UseOAuthAuthorizationServer(new OAuthAuthorizationServerOptions()
{
    AllowInsecureHttp = true,
    TokenEndpointPath = new PathString("/token"),
    AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(5),
    Provider = new SimpleAuthorizationServerProvider(new SimpleAuthorizationServerProviderOptions()
    {
        ValidateUserCredentialsFunction = ValidateUser
    }),
    RefreshTokenProvider = new SimpleRefreshTokenProvider()
});

This is my SimpleAuthorizationServerProviderOptions implementation:

public class SimpleAuthorizationServerProvider : OAuthAuthorizationServerProvider
{
    public delegate Task<bool> ClientCredentialsValidationFunction(string clientid, string secret);
    public delegate Task<IEnumerable<Claim>> UserCredentialValidationFunction(string username, string password);
    public SimpleAuthorizationServerProviderOptions Options { get; private set; }

    public SimpleAuthorizationServerProvider(SimpleAuthorizationServerProviderOptions options)
    {
        if (options.ValidateUserCredentialsFunction == null)
        {
            throw new NullReferenceException("ValidateUserCredentialsFunction cannot be null");
        }
        Options = options;
    }

    public SimpleAuthorizationServerProvider(UserCredentialValidationFunction userCredentialValidationFunction)
    {
        Options = new SimpleAuthorizationServerProviderOptions()
        {
            ValidateUserCredentialsFunction = userCredentialValidationFunction
        };
    }

    public SimpleAuthorizationServerProvider(UserCredentialValidationFunction userCredentialValidationFunction, ClientCredentialsValidationFunction clientCredentialsValidationFunction)
    {
        Options = new SimpleAuthorizationServerProviderOptions()
        {
            ValidateUserCredentialsFunction = userCredentialValidationFunction,
            ValidateClientCredentialsFunction = clientCredentialsValidationFunction
        };
    }

    public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
    {
        if (Options.ValidateClientCredentialsFunction != null)
        {
            string clientId, clientSecret;

            if (!context.TryGetBasicCredentials(out clientId, out clientSecret))
            {
                context.TryGetFormCredentials(out clientId, out clientSecret);
            }

            var clientValidated = await Options.ValidateClientCredentialsFunction(clientId, clientSecret);
            if (!clientValidated)
            {
                context.Rejected();
                return;
            }
        }

        context.Validated();
    }

    public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
    {
        if (Options.ValidateUserCredentialsFunction == null)
        {
            throw new NullReferenceException("ValidateUserCredentialsFunction cannot be null");
        }

        var claims = await Options.ValidateUserCredentialsFunction(context.UserName, context.Password);
        if (claims == null)
        {
            context.Rejected();
            return;
        }

        // create identity
        var identity = new ClaimsIdentity(claims, context.Options.AuthenticationType);

        // create metadata to pass to refresh token provider
        var props = new AuthenticationProperties(new Dictionary<string, string>()
        {
            { "as:client_id", context.UserName }
        });

        var ticket = new AuthenticationTicket(identity, props);
        context.Validated(ticket);
    }

    public override async Task GrantRefreshToken(OAuthGrantRefreshTokenContext context)
    {
        var originalClient = context.Ticket.Properties.Dictionary["as:client_id"];
        var currentClient = context.ClientId;

        // enforce client binding of refresh token
        if (originalClient != currentClient)
        {
            context.Rejected();
            return;
        }

        // chance to change authentication ticket for refresh token requests
        var newIdentity = new ClaimsIdentity(context.Ticket.Identity);
        newIdentity.AddClaim(new Claim("newClaim", "refreshToken"));

        var newTicket = new AuthenticationTicket(newIdentity, context.Ticket.Properties);
        context.Validated(newTicket);
    }
}

And my SimpleRefreshTokenProvider implementation:

public class SimpleRefreshTokenProvider : IAuthenticationTokenProvider
{
    private static ConcurrentDictionary<string, AuthenticationTicket> _refreshTokens =
        new ConcurrentDictionary<string, AuthenticationTicket>(); 

    public void Create(AuthenticationTokenCreateContext context)
    {

    }

    public async Task CreateAsync(AuthenticationTokenCreateContext context)
    {
        var guid = Guid.NewGuid().ToString();

        var refreshTokenProperties = new AuthenticationProperties(context.Ticket.Properties.Dictionary)
        {
            IssuedUtc = context.Ticket.Properties.IssuedUtc,
            ExpiresUtc = DateTime.UtcNow.AddYears(1)
        };
        var refreshTokenTicket = new AuthenticationTicket(context.Ticket.Identity, refreshTokenProperties);

        _refreshTokens.TryAdd(guid, refreshTokenTicket);
        context.SetToken(guid);
    }

    public void Receive(AuthenticationTokenReceiveContext context)
    {

    }

    public async Task ReceiveAsync(AuthenticationTokenReceiveContext context)
    {
        AuthenticationTicket ticket;
        if (_refreshTokens.TryRemove(context.Token, out ticket))
        {
            context.SetTicket(ticket);
        }
    }
}

What I don't fully understand is the use of ClientId and Secret vs Username and Password. The code I pasted generates a token by username and password and I can work with that token (until it expires), but when I try to get a refresh token, I must have the ClientId.

Also, if a token expires, the correct way is to send the refresh token and get a new token? What if the refresh token gets stolen? isn't it the same as a username & password getting stolen?

解决方案

What I don't fully understand is the use of ClientId and Secret vs Username and Password. The code I pasted generates a token by username and password and I can work with that token (until it expires), but when I try to get a refresh token, I must have the ClientId.

Also, if a token expires, the correct way is to send the refresh token and get a new token? What if the refresh token gets stolen? isn't it the same as a username & password getting stolen?

In OAuth2 is essential to authenticate both the user and the client in any authorization flow defined by the protocol. The client authentication (as you may guess) enforces the use of your API only by known clients. The serialized access token, once generated, is not bound to a specific client directly. Please note that the ClientSecret must be treated as a confidential information, and can be used only by clients that can store this information in some secure way (e.g. external services clients, but not javascript clients).

The refresh token is simply an alternative "grant type" for OAuth2, and, as you stated correctly, will substitute the username and password pair for a User. This token must be treated as confidential data (even more confidential than the access token), but gives advantages over storing the username & password on the client:

  • it can be revoked by the user if compromised;
  • it has a limited lifetime (usually days or weeks);
  • it does not expose user credentials (an attacker can only get access tokens for the "scope" the refresh token was issued).

I suggest you to read more about the different grant types defined in OAuth 2 checking in the official draft. I also recommend you this resource I found very useful when firstly implemented OAuth2 in Web API myself.

Sample requests

Here are two request examples using fiddler, for Resource Owner Password Credentials Grant:

and for Refresh Token Grant:

这篇关于ASP.NET OAuth授权 - 使用客户端Id和秘密及用户名和密码的区别的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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