Ich verwende die Web Visual API 2-Vorlage, die mit Visual Studio 2013 geliefert wird. Sie verfügt über eine OWIN-Middleware für die Benutzerauthentifizierung und dergleichen.
In der habe OAuthAuthorizationServerOptions
ich festgestellt, dass der OAuth2-Server so eingerichtet ist, dass Token ausgegeben werden, die in 14 Tagen ablaufen
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
};
Dies ist nicht für mein aktuelles Projekt geeignet. Ich möchte kurzlebige bearer_tokens verteilen, die mit a aktualisiert werden könnenrefresh_token
Ich habe viel gegoogelt und kann nichts hilfreiches finden.
So weit bin ich also gekommen. Ich habe jetzt den Punkt "WTF mache ich jetzt" erreicht.
Ich habe eine geschrieben RefreshTokenProvider
, die IAuthenticationTokenProvider
gemäß der RefreshTokenProvider
Eigenschaft auf OAuthAuthorizationServerOptions
Klasse implementiert :
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
};
Wenn also jemand eine anfordert, bearer_token
sende ich jetzt eine refresh_token
, was großartig ist.
Wie verwende ich nun dieses refresh_token, um ein neues zu erhalten bearer_token
? Vermutlich muss ich eine Anfrage an meinen Token-Endpunkt senden, wobei bestimmte HTTP-Header festgelegt sind.
Ich denke nur laut nach, während ich tippe ... Soll ich den Ablauf von refresh_token in meinem behandeln SimpleRefreshTokenProvider
? Wie würde ein Kunde einen neuen erhalten refresh_token
?
Ich könnte wirklich etwas Lesematerial / Dokumentation gebrauchen, weil ich das nicht falsch verstehen möchte und einer Art Standard folgen möchte.
quelle
Antworten:
Ich habe gerade meinen OWIN-Dienst mit Bearer (im Folgenden access_token genannt) implementiert und Tokens aktualisiert. Mein Einblick in diese ist, dass Sie verschiedene Flows verwenden können. Es hängt also von dem Ablauf ab, den Sie verwenden möchten, wie Sie die Ablaufzeiten für access_token und refresh_token festlegen.
Ich werde im Folgenden zwei Flüsse A und B beschreiben (ich schlage vor, Sie möchten Fluss B haben):
A) Die Ablaufzeit von access_token und refresh_token ist dieselbe wie standardmäßig 1200 Sekunden oder 20 Minuten. Für diesen Ablauf muss Ihr Client zuerst client_id und client_secret mit Anmeldedaten senden, um ein access_token, ein refresh_token und eine expiration_time zu erhalten. Mit dem refresh_token ist es jetzt möglich, ein neues access_token für 20 Minuten abzurufen (oder was auch immer Sie das AccessTokenExpireTimeSpan in den OAuthAuthorizationServerOptions auf setzen). Aus dem Grund, dass die Ablaufzeit von access_token und refresh_token gleich ist, ist Ihr Client dafür verantwortlich, vor Ablauf der Ablaufzeit ein neues access_token zu erhalten! Beispielsweise könnte Ihr Client einen Aktualisierungs-POST-Aufruf mit dem Body an Ihren Token-Endpunkt senden (Anmerkung: Sie sollten https in der Produktion verwenden).
um nach zB 19 Minuten einen neuen Token zu erhalten, um zu verhindern, dass die Token ablaufen.
B) In diesem Ablauf möchten Sie einen kurzfristigen Ablauf für Ihr access_token und einen langfristigen Ablauf für Ihr refresh_token haben. Nehmen wir zu Testzwecken an, Sie setzen das access_token so, dass es in 10 Sekunden abläuft (
AccessTokenExpireTimeSpan = TimeSpan.FromSeconds(10)
) und das refresh_token auf 5 Minuten. Nun kommt es zu dem interessanten Teil, der die Ablaufzeit von refresh_token festlegt: Sie tun dies in Ihrer createAsync-Funktion in der SimpleRefreshTokenProvider-Klasse wie folgt: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);
Jetzt kann Ihr Client einen POST-Aufruf mit einem refresh_token an Ihren Token-Endpunkt senden, wenn der
access_token
abgelaufen ist. Der Hauptteil des Anrufs kann folgendermaßen aussehen:grant_type=refresh_token&client_id=xxxxxx&refresh_token=xxxxxxxx-xxxx-xxxx-xxxx-xx
Eine wichtige Sache ist, dass Sie diesen Code möglicherweise nicht nur in Ihrer CreateAsync-Funktion, sondern auch in Ihrer Create-Funktion verwenden möchten. Sie sollten daher in Betracht ziehen, Ihre eigene Funktion (z. B. CreateTokenInternal) für den obigen Code zu verwenden. Hier finden Sie Implementierungen verschiedener Flows, einschließlich refresh_token-Flow (ohne jedoch die Ablaufzeit des refresh_token festzulegen).
Hier ist eine Beispielimplementierung von IAuthenticationTokenProvider auf github (mit Festlegen der Ablaufzeit des refresh_token)
Es tut mir leid, dass ich mit weiteren Materialien als den OAuth-Spezifikationen und der Microsoft API-Dokumentation nicht weiterhelfen kann. Ich würde die Links hier posten, aber mein Ruf lässt mich nicht mehr als 2 Links posten ....
Ich hoffe, dies kann einigen anderen helfen, Zeit zu sparen, wenn sie versuchen, OAuth2.0 mit einer Ablaufzeit von refresh_token zu implementieren, die sich von der Ablaufzeit von access_token unterscheidet. Ich konnte keine Beispielimplementierung im Web finden (außer der oben verlinkten von thinktecture) und es dauerte einige Stunden, bis ich für sie funktionierte.
Neue Info: In meinem Fall habe ich zwei verschiedene Möglichkeiten, Token zu erhalten. Eine besteht darin, ein gültiges access_token zu erhalten. Dort muss ich einen POST-Aufruf mit einem String-Body im Format application / x-www-form-urlencoded mit den folgenden Daten senden
Zweitens, wenn access_token nicht mehr gültig ist, können wir das refresh_token versuchen, indem wir einen POST-Aufruf mit einem String-Body im Format
application/x-www-form-urlencoded
mit den folgenden Daten sendengrant_type=refresh_token&client_id=YOURCLIENTID&refresh_token=YOURREFRESHTOKENGUID
quelle
RefreshTokens
. WennRefreshTokens
also ein Leck durchgesickert ist, kann ein Angreifer diese Informationen nicht verwenden!?Sie müssen RefreshTokenProvider implementieren . Erstellen Sie zuerst eine Klasse für RefreshTokenProvider, dh.
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); } }
Fügen Sie dann eine Instanz zu OAuthOptions hinzu .
OAuthOptions = new OAuthAuthorizationServerOptions { TokenEndpointPath = new PathString("/authenticate"), Provider = new ApplicationOAuthProvider(), AccessTokenExpireTimeSpan = TimeSpan.FromSeconds(expire), RefreshTokenProvider = new ApplicationRefreshTokenProvider() };
quelle
context.OwinContext.Environment
enthält einenMicrosoft.Owin.Form#collection
Schlüssel, mit dem SieFormCollection
den Grant-Typ finden und einen entsprechenden Token hinzufügen können. Die Implementierung ist undicht, kann bei zukünftigen Updates jederzeit beschädigt werden, und ich bin mir nicht sicher, ob sie zwischen OWIN-Hosts portierbar ist.var form = await context.Request.ReadFormAsync();
var grantType = form.GetValue("grant_type");
dann das Aktualisierungstoken aus, wenn der Grant-Typ nicht "refresh_token" istIch denke nicht, dass Sie ein Array verwenden sollten, um Token zu verwalten. Sie brauchen auch keinen Guid als Token.
Sie können context.SerializeTicket () problemlos verwenden.
Siehe meinen folgenden Code.
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); } }
quelle
Freddys Antwort hat mir sehr geholfen, das zum Laufen zu bringen. Der Vollständigkeit halber können Sie hier das Hashing des Tokens implementieren:
private string ComputeHash(Guid input) { byte[] source = input.ToByteArray(); var encoder = new SHA256Managed(); byte[] encoded = encoder.ComputeHash(source); return Convert.ToBase64String(encoded); }
In
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); } } }
quelle