Oauth 2.0 在JavaScript中存储刷新令牌,获取新的访问令牌
我有一个ASP.NET Web API,它在登录时返回OAuth2承载令牌。我计划通过JavaScript将刷新令牌存储在cookie中。在我的JS中,我应该在每次需要身份验证的后续API调用之前或在某种计时器循环中检查访问令牌是否过期以获取新令牌?这将是一个异步web应用程序,所以我认为计时器循环并不理想。有什么想法吗?我不会这么做,如果你能用javascript访问你的cookie,那就意味着任何XSS脚本都可以劫持它。本地存储也不是一个安全的地方 我建议在服务器上生成令牌之后立即将令牌存储在安全的仅http cookie中,并将该cookie附加到响应中 如果将WebAPI 2与oAuth一起使用,则可以在authorizationServer提供程序中覆盖TokenEndpointResponseOauth 2.0 在JavaScript中存储刷新令牌,获取新的访问令牌,oauth-2.0,Oauth 2.0,我有一个ASP.NET Web API,它在登录时返回OAuth2承载令牌。我计划通过JavaScript将刷新令牌存储在cookie中。在我的JS中,我应该在每次需要身份验证的后续API调用之前或在某种计时器循环中检查访问令牌是否过期以获取新令牌?这将是一个异步web应用程序,所以我认为计时器循环并不理想。有什么想法吗?我不会这么做,如果你能用javascript访问你的cookie,那就意味着任何XSS脚本都可以劫持它。本地存储也不是一个安全的地方 我建议在服务器上生成令牌之后立即将令牌存储
/// <summary>
/// Called when a request to the Token endpoint is finished and gonna be sent back to the client
/// We intercept the token and force the client to write a cookie containing the token value.
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public override System.Threading.Tasks.Task TokenEndpointResponse(OAuthTokenEndpointResponseContext context)
{
// We set our auth cookie configuration
var cookieOptions = new CookieOptions
{
HttpOnly = true, // immune to JS manipulation
Secure = insertMagicLogicHere(), // we set the cookie to secure in production environment
Path = "/",
Domain = ".mycooldomain.com",
Expires = DateTime.Now.AddMinutes(GlobalAuthSettings.AuthServerOptions.AccessTokenExpireTimeSpan.TotalMinutes)
};
var refreshTokenId = context.OwinContext.Get<string>("as:refreshTokenId");
// We build the authentication object to store in our cookie
var ourTokenObject = new AuthCookie
{
username = context.Properties.Dictionary["userName"],
token = context.AccessToken,
useRefreshTokens = true,
refreshtoken = refreshTokenId
};
// We send it back
context.Response.Cookies.Append("mySecureCookie", JsonConvert.SerializeObject(ourTokenObject), cookieOptions);
return base.TokenEndpointResponse(context);
}
//
///当对令牌端点的请求完成并将被发送回客户端时调用
///我们截取令牌并强制客户端编写包含令牌值的cookie。
///
///
///
公共重写System.Threading.Tasks.Task TokenEndpointResponse(OAuthTokenEndpointResponseContext)
{
//我们设置了auth cookie配置
var cookieOptions=新的cookieOptions
{
HttpOnly=true,//对JS操作免疫
Secure=insertMagicLogicHere(),//我们在生产环境中将cookie设置为安全的
Path=“/”,
Domain=“.mycoldomain.com”,
Expires=DateTime.Now.AddMinutes(GlobalAuthSettings.AuthServerOptions.AccessTokenExpireTimeSpan.TotalMinutes)
};
var refreshttokenid=context.OwinContext.Get(“as:refreshttokenid”);
//我们构建要存储在cookie中的身份验证对象
var ourTokenObject=new AuthCookie
{
username=context.Properties.Dictionary[“username”],
token=context.AccessToken,
useRefreshTokens=true,
refreshtoken=refreshTokenId
};
//我们把它寄回去
context.Response.Cookies.Append(“mysecurecokie”,JsonConvert.SerializeObject(ourTokenObject),cookieOptions);
返回base.TokenEndpointResponse(上下文);
}
然后在javascript中,您需要确保cookie随每个请求一起发送。
您可以设置一些Owin中间件来拦截请求,从cookie解析令牌,并将令牌设置为授权头
要刷新令牌,您可以配置一个Http拦截器,该拦截器将在收到401时自动刷新令牌,并在刷新令牌成功后重试请求
下面是一些代码
//#region Private members
var _retryHttpRequest = function (config, deferred) {
$rootScope.httpRetries++;
console.log('autorefresh');
$http = $http || $injector.get('$http') || $injector.post('$http');
$http(config).then(
function (success) {
$rootScope.httpRetries--;
deferred.resolve(success);
},
function (error) {
console.log("Error in response", rejection);
deferred.reject(error);
});
};
var _refreshToken = function () {
var deferred = $q.defer();
// refresh_token=refreshToken because the refresh_token is serialized in the http cookie and not accessible in javascript, the refreshToken is however stored in the cookie so we can resolve that server side
var data = "grant_type=refresh_token&refresh_token=refreshToken&client_id=" + appState.clientId;
$http = $http || $injector.get('$http');
$http.post(authUrl + '/token', data, {
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
}).success(function (response) {
deferred.resolve(response);
}).error(function (err, status) {
deferred.reject(err);
});
return deferred.promise;
};
//#endregion Private members
//#region Public members
function _request(config) {
config.withCredentials = true; // forces angular to send cookies in each request
return config;
}
// intercept response errors and refresh token if need be
function _responseError(rejection) {
var deferred = $q.defer();
if (rejection.status === 401 || rejection.status === 403) {
if ($rootScope.httpRetries < 2) {
console.log("calling refreshToken()");
_refreshToken().then(function (response) {
console.log("token refreshed, retrying to connect");
// retry the request if the token was successfully refreshed
_retryHttpRequest(rejection.config, deferred);
}, function (error) {
console.log("_refreshToken error", error);
deferred.reject(rejection);
window.location.reload(true);
});
}
else {
console.log("_refreshToken error", error);
deferred.reject(rejection);
window.location.reload(true);
}
} else {
console.log("Error in response", rejection);
deferred.reject(rejection);
}
return deferred.promise;
};
//#endregion Public members
/#地区私人会员
var\u retryHttpRequest=函数(配置,延迟){
$rootScope.httpRetries++;
console.log('autorefresh');
$http=$http | |$injector.get('http')| |$injector.post('http');
$http(config)。然后(
功能(成功){
$rootScope.httpRetries--;
决心(成功);
},
函数(错误){
日志(“响应错误”,拒绝);
延迟。拒绝(错误);
});
};
var_refreshttoken=函数(){
var deferred=$q.deferred();
//refresh_token=refreshToken因为refresh_token在http cookie中序列化,在javascript中不可访问,所以refreshToken存储在cookie中,因此我们可以解析该服务器端
var data=“grant\u type=refresh\u-token&refresh\u-token=refreshttoken&client\u-id=“+appState.clientId;
$http=$http | |$injector.get(“$http”);
$http.post(authUrl+'/token',数据{
标题:{'Content Type':'application/x-www-form-urlencoded'}
}).成功(功能(响应){
延迟。解决(响应);
}).错误(功能(错误、状态){
延迟。拒绝(错误);
});
回报。承诺;
};
//#端域私有成员
//#地区公众成员
函数_请求(配置){
config.withCredentials=true;//强制angular在每个请求中发送cookie
返回配置;
}
//拦截响应错误并在需要时刷新令牌
功能响应错误(拒绝){
var deferred=$q.deferred();
如果(拒绝.status==401 | |拒绝.status==403){
if($rootScope.httpRetries<2){
log(“调用refreshToken()”);
_refreshToken()。然后(函数(响应){
log(“令牌已刷新,正在尝试连接”);
//如果令牌已成功刷新,请重试请求
_retryHttpRequest(rejection.config,deferred);
},函数(错误){
日志(“\u刷新令牌错误”,错误);
延期。拒绝(拒绝);
window.location.reload(true);
});
}
否则{
日志(“\u刷新令牌错误”,错误);
延期。拒绝(拒绝);
window.location.reload(true);
}
}否则{
日志(“响应错误”,拒绝);
延期。拒绝(拒绝);
}
回报。承诺;
};
//#endregion公共成员
希望这有帮助。我不会这么做,如果你能用javascript访问cookie,那就意味着任何XSS脚本都可以劫持它。本地存储也不是一个安全的地方 我建议在服务器上生成令牌之后立即将令牌存储在安全的仅http cookie中,并将该cookie附加到响应中 如果将WebAPI 2与oAuth一起使用,则可以在authorizationServer提供程序中覆盖TokenEndpointResponse
/// <summary>
/// Called when a request to the Token endpoint is finished and gonna be sent back to the client
/// We intercept the token and force the client to write a cookie containing the token value.
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public override System.Threading.Tasks.Task TokenEndpointResponse(OAuthTokenEndpointResponseContext context)
{
// We set our auth cookie configuration
var cookieOptions = new CookieOptions
{
HttpOnly = true, // immune to JS manipulation
Secure = insertMagicLogicHere(), // we set the cookie to secure in production environment
Path = "/",
Domain = ".mycooldomain.com",
Expires = DateTime.Now.AddMinutes(GlobalAuthSettings.AuthServerOptions.AccessTokenExpireTimeSpan.TotalMinutes)
};
var refreshTokenId = context.OwinContext.Get<string>("as:refreshTokenId");
// We build the authentication object to store in our cookie
var ourTokenObject = new AuthCookie
{
username = context.Properties.Dictionary["userName"],
token = context.AccessToken,
useRefreshTokens = true,
refreshtoken = refreshTokenId
};
// We send it back
context.Response.Cookies.Append("mySecureCookie", JsonConvert.SerializeObject(ourTokenObject), cookieOptions);
return base.TokenEndpointResponse(context);
}
//
///Ca