OWIN 보안-OAuth2 새로 고침 토큰 구현 방법
Visual Studio 2013과 함께 제공되는 Web Api 2 템플릿을 사용하고 있으며 사용자 인증 등을 수행하는 일부 OWIN 미들웨어가 있습니다.
에서 OAuthAuthorizationServerOptions
발견 i를 OAuth2를 서버 14 일에 만료 토큰 밖으로 손에 설치가 있음
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/api/token"),
Provider = new ApplicationOAuthProvider(PublicClientId,UserManagerFactory) ,
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
AllowInsecureHttp = true
};
이것은 내 최신 프로젝트에 적합하지 않습니다. 사용하여 새로 고칠 수있는 수명이 짧은 bearer_token을 배포하고 싶습니다.refresh_token
인터넷 검색을 많이했는데 도움이되는 것을 찾을 수 없습니다.
그래서 이것은 내가 얼마나 멀리 얻을 수 있었는지입니다. 나는 이제 "WTF do I now"의 지점에 도달했습니다.
클래스 의 속성에 따라 RefreshTokenProvider
구현 하는를 작성했습니다 .IAuthenticationTokenProvider
RefreshTokenProvider
OAuthAuthorizationServerOptions
public class SimpleRefreshTokenProvider : IAuthenticationTokenProvider
{
private static ConcurrentDictionary<string, AuthenticationTicket> _refreshTokens = new ConcurrentDictionary<string, AuthenticationTicket>();
public async Task CreateAsync(AuthenticationTokenCreateContext context)
{
var guid = Guid.NewGuid().ToString();
_refreshTokens.TryAdd(guid, context.Ticket);
// hash??
context.SetToken(guid);
}
public async Task ReceiveAsync(AuthenticationTokenReceiveContext context)
{
AuthenticationTicket ticket;
if (_refreshTokens.TryRemove(context.Token, out ticket))
{
context.SetTicket(ticket);
}
}
public void Create(AuthenticationTokenCreateContext context)
{
throw new NotImplementedException();
}
public void Receive(AuthenticationTokenReceiveContext context)
{
throw new NotImplementedException();
}
}
// Now in my Startup.Auth.cs
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/api/token"),
Provider = new ApplicationOAuthProvider(PublicClientId,UserManagerFactory) ,
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(2),
AllowInsecureHttp = true,
RefreshTokenProvider = new RefreshTokenProvider() // This is my test
};
그래서 이제 누군가가를 요청할 때 bearer_token
나는 지금을 보내고 refresh_token
있습니다.
이제이 refresh_token을 사용하여 새를 가져 오는 방법은 bearer_token
무엇입니까? 아마도 특정 HTTP 헤더가 설정된 토큰 끝점에 요청을 보내야 할 것입니다.
입력 할 때 큰 소리로 생각하는 중 ... 내에서 refresh_token 만료를 처리해야합니까 SimpleRefreshTokenProvider
? 클라이언트는 어떻게 새로운 것을 얻을 수 refresh_token
있습니까?
나는 이것을 잘못하고 싶지 않고 일종의 표준을 따르고 싶기 때문에 독서 자료 / 문서로 정말 할 수 있습니다.
Bearer (다음에서 access_token이라고 함) 및 새로 고침 토큰으로 OWIN 서비스를 구현했습니다. 이에 대한 나의 통찰력은 다른 흐름을 사용할 수 있다는 것입니다. 따라서 access_token 및 refresh_token 만료 시간을 설정하는 방법을 사용하려는 흐름에 따라 다릅니다.
다음에서 두 가지 흐름 A 와 B 를 설명하겠습니다 (원하는 것은 흐름 B라고 제안합니다).
A) access_token 및 refresh_token의 만료 시간은 기본 1200 초 또는 20 분당과 동일합니다. 이 흐름에서는 클라이언트가 먼저 access_token, refresh_token 및 expire_time을 가져 오기 위해 로그인 데이터와 함께 client_id 및 client_secret을 보내야합니다. refresh_token을 사용하면 이제 20 분 동안 새 access_token을 얻을 수 있습니다 (또는 OAuthAuthorizationServerOptions에서 AccessTokenExpireTimeSpan을 설정 한대로). access_token과 refresh_token의 만료 시간이 동일하기 때문에 클라이언트는 만료 시간 전에 새로운 access_token을 받아야합니다! 예를 들어 클라이언트가 본문과 함께 토큰 엔드 포인트에 새로 고침 POST 호출을 보낼 수 있습니다 (참고 : 프로덕션에서 https를 사용해야 함).
grant_type=refresh_token&client_id=xxxxxx&refresh_token=xxxxxxxx-xxxx-xxxx-xxxx-xxxxx
토큰이 만료되는 것을 방지하기 위해 예를 들어 19 분 후에 새 토큰을 얻습니다.
B) 이 흐름에서 access_token에 대한 단기 만료 및 refresh_token에 대한 장기 만료를 원합니다. 테스트 목적으로 access_token을 10 초 ( AccessTokenExpireTimeSpan = TimeSpan.FromSeconds(10)
) 에 만료되도록 설정 하고 refresh_token을 5 분으로 설정했다고 가정 해 보겠습니다 . 이제 refresh_token의 만료 시간을 설정하는 흥미로운 부분이 있습니다. 다음과 같이 SimpleRefreshTokenProvider 클래스의 createAsync 함수에서이 작업을 수행합니다.
var guid = Guid.NewGuid().ToString();
//copy properties and set the desired lifetime of refresh token
var refreshTokenProperties = new AuthenticationProperties(context.Ticket.Properties.Dictionary)
{
IssuedUtc = context.Ticket.Properties.IssuedUtc,
ExpiresUtc = DateTime.UtcNow.AddMinutes(5) //SET DATETIME to 5 Minutes
//ExpiresUtc = DateTime.UtcNow.AddMonths(3)
};
/*CREATE A NEW TICKET WITH EXPIRATION TIME OF 5 MINUTES
*INCLUDING THE VALUES OF THE CONTEXT TICKET: SO ALL WE
*DO HERE IS TO ADD THE PROPERTIES IssuedUtc and
*ExpiredUtc to the TICKET*/
var refreshTokenTicket = new AuthenticationTicket(context.Ticket.Identity, refreshTokenProperties);
//saving the new refreshTokenTicket to a local var of Type ConcurrentDictionary<string,AuthenticationTicket>
// consider storing only the hash of the handle
RefreshTokens.TryAdd(guid, refreshTokenTicket);
context.SetToken(guid);
이제 클라이언트 access_token
는 만료 될 때 토큰 엔드 포인트에 refresh_token이 포함 된 POST 호출을 보낼 수 있습니다. 호출의 본문 부분은 다음과 같습니다.grant_type=refresh_token&client_id=xxxxxx&refresh_token=xxxxxxxx-xxxx-xxxx-xxxx-xx
한 가지 중요한 점은 CreateAsync 함수뿐만 아니라 Create 함수에서도이 코드를 사용할 수 있다는 것입니다. 따라서 위 코드에 대해 자체 함수 (예 : CreateTokenInternal)를 사용하는 것을 고려해야합니다. 여기에서 refresh_token 흐름을 포함한 다양한 흐름의 구현을 찾을 수 있습니다 (그러나 refresh_token 의 만료 시간은 설정하지 않음).
다음은 github의 IAuthenticationTokenProvider 구현 샘플입니다 (refresh_token의 만료 시간 설정 포함).
OAuth 사양 및 Microsoft API 문서 이외의 자료로 도움을 드릴 수 없어서 죄송합니다. 나는 여기에 링크를 게시 할 것이지만 내 평판은 내가 2 개 이상의 링크를 게시하도록 허용하지 않는다 ....
나는 이것이 access_token 만료 시간과 다른 refresh_token 만료 시간으로 OAuth2.0을 구현하려고 할 때 다른 사람들이 시간을 할애하는 데 도움이되기를 바랍니다. 웹에서 예제 구현을 찾을 수 없었고 (위에 링크 된 thinktecture 중 하나를 제외하고) 저에게 효과가있을 때까지 조사하는 데 몇 시간이 걸렸습니다.
새로운 정보 : 제 경우에는 토큰을받을 수있는 두 가지 다른 가능성이 있습니다. 하나는 유효한 access_token을받는 것입니다. 거기에 다음 데이터로 application / x-www-form-urlencoded 형식의 문자열 본문이있는 POST 호출을 보내야합니다.
client_id=YOURCLIENTID&grant_type=password&username=YOURUSERNAME&password=YOURPASSWORD
두 번째는 access_token이 더 이상 유효하지 않은 경우 application/x-www-form-urlencoded
다음 데이터가 포함 된 형식의 문자열 본문과 함께 POST 호출을 전송하여 refresh_token을 시도 할 수 있습니다.grant_type=refresh_token&client_id=YOURCLIENTID&refresh_token=YOURREFRESHTOKENGUID
RefreshTokenProvider 를 구현해야합니다 . 먼저 RefreshTokenProvider에 대한 클래스를 만듭니다.
public class ApplicationRefreshTokenProvider : AuthenticationTokenProvider
{
public override void Create(AuthenticationTokenCreateContext context)
{
// Expiration time in seconds
int expire = 5*60;
context.Ticket.Properties.ExpiresUtc = new DateTimeOffset(DateTime.Now.AddSeconds(expire));
context.SetToken(context.SerializeTicket());
}
public override void Receive(AuthenticationTokenReceiveContext context)
{
context.DeserializeTicket(context.Token);
}
}
그런 다음 OAuthOptions 에 인스턴스를 추가합니다 .
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/authenticate"),
Provider = new ApplicationOAuthProvider(),
AccessTokenExpireTimeSpan = TimeSpan.FromSeconds(expire),
RefreshTokenProvider = new ApplicationRefreshTokenProvider()
};
토큰을 유지하기 위해 배열을 사용해야한다고 생각하지 않습니다. 토큰으로 GUID가 필요하지 않습니다.
context.SerializeTicket ()을 쉽게 사용할 수 있습니다.
아래 코드를 참조하십시오.
public class RefreshTokenProvider : IAuthenticationTokenProvider
{
public async Task CreateAsync(AuthenticationTokenCreateContext context)
{
Create(context);
}
public async Task ReceiveAsync(AuthenticationTokenReceiveContext context)
{
Receive(context);
}
public void Create(AuthenticationTokenCreateContext context)
{
object inputs;
context.OwinContext.Environment.TryGetValue("Microsoft.Owin.Form#collection", out inputs);
var grantType = ((FormCollection)inputs)?.GetValues("grant_type");
var grant = grantType.FirstOrDefault();
if (grant == null || grant.Equals("refresh_token")) return;
context.Ticket.Properties.ExpiresUtc = DateTime.UtcNow.AddDays(Constants.RefreshTokenExpiryInDays);
context.SetToken(context.SerializeTicket());
}
public void Receive(AuthenticationTokenReceiveContext context)
{
context.DeserializeTicket(context.Token);
if (context.Ticket == null)
{
context.Response.StatusCode = 400;
context.Response.ContentType = "application/json";
context.Response.ReasonPhrase = "invalid token";
return;
}
if (context.Ticket.Properties.ExpiresUtc <= DateTime.UtcNow)
{
context.Response.StatusCode = 401;
context.Response.ContentType = "application/json";
context.Response.ReasonPhrase = "unauthorized";
return;
}
context.Ticket.Properties.ExpiresUtc = DateTime.UtcNow.AddDays(Constants.RefreshTokenExpiryInDays);
context.SetTicket(context.Ticket);
}
}
Freddy의 답변 은이 작업을 수행하는 데 많은 도움이되었습니다. 완전성을 위해 토큰 해싱을 구현하는 방법은 다음과 같습니다.
private string ComputeHash(Guid input)
{
byte[] source = input.ToByteArray();
var encoder = new SHA256Managed();
byte[] encoded = encoder.ComputeHash(source);
return Convert.ToBase64String(encoded);
}
에서 CreateAsync
:
var guid = Guid.NewGuid();
...
_refreshTokens.TryAdd(ComputeHash(guid), refreshTokenTicket);
context.SetToken(guid.ToString());
ReceiveAsync
:
public async Task ReceiveAsync(AuthenticationTokenReceiveContext context)
{
Guid token;
if (Guid.TryParse(context.Token, out token))
{
AuthenticationTicket ticket;
if (_refreshTokens.TryRemove(ComputeHash(token), out ticket))
{
context.SetTicket(ticket);
}
}
}
참고 URL : https://stackoverflow.com/questions/20637674/owin-security-how-to-implement-oauth2-refresh-tokens
'IT TIP' 카테고리의 다른 글
Gradle을 통해 Java 클래스를 실행할 때 시스템 속성 및 매개 변수 전달 문제 (0) | 2020.10.16 |
---|---|
OpenGraph 또는 Schema.org? (0) | 2020.10.16 |
컴포넌트의 Reactjs 비동기 렌더링 (0) | 2020.10.16 |
Android 스튜디오 gradle.properties 파일은 어디에 있습니까? (0) | 2020.10.16 |
Visual Studio 2017에서 Microsoft.NETCore.App 패키지를 업데이트 할 수 없습니다 ( "프로젝트에서 차단됨"). (0) | 2020.10.16 |