Ich habe einen Autorisierungsserver nach dem ArtikelWo überprüft OAuthAuthorizationServerProvider das Token-Ablaufdatum aktualisieren?
ich es fast genau wie der Artikel implementiert habe umgesetzt, aber ich sehe nicht, wie der Auth-Server weiß, werden die Aktualisierungs-Token abgelaufen. Tatsächlich habe ich getestet und der Server gewährt keine Zugriffstoken, wenn abgelaufene Aktualisierungstoken angezeigt werden, aber ich sehe die Logik dafür in meinem Authentifizierungsserver nicht. Wenn ich ein Zugriffstoken unter Verwendung eines abgelaufenen Aktualisierungstokens anfordere, wird die Unterklasse my OAuthAuthorizationServerProvider nie aufgerufen. Tatsächlich wird keine meiner Methoden in meiner abgeleiteten Klasse OAuthAuthorizationServerProvider oder meiner IAuthenticationTokenProvider-Implementierung aufgerufen, wenn ich ein neues Zugriffstoken mit einem abgelaufenen Token anfordere Aktualisierungstoken Jede Hilfe wird geschätzt. Hier ist, was ich habe
public class SmartCardOAuthAuthenticationTokenProvider : IAuthenticationTokenProvider
{
private IDataAccessFactoryFactory _producesFactoryThatProducesIAuthenticateDataAccess;
public SmartCardOAuthAuthenticationTokenProvider(IDataAccessFactoryFactory producesFactoryThatProducesIAuthenticateDataAccess)
{
_producesFactoryThatProducesIAuthenticateDataAccess = producesFactoryThatProducesIAuthenticateDataAccess;
}
public async Task CreateAsync(AuthenticationTokenCreateContext context)
{
var clientid = context.Ticket.Properties.Dictionary["as:client_id"];
if (string.IsNullOrEmpty(clientid))
{
return;
}
var refreshTokenId = Guid.NewGuid().ToString("n");
using(IDataAccessFactory producesIAuthenticateDataAccess = _producesFactoryThatProducesIAuthenticateDataAccess.GetDataAccessFactory())//using (IAuthorizationDataAccess _repo = new AuthRepository())
{
IAuthorizationDataAccess _repo = producesIAuthenticateDataAccess.GetDataAccess<IAuthorizationDataAccess>();
var refreshTokenLifeTime = context.OwinContext.Get<string>("as:clientRefreshTokenLifeTime");
var token = new RefreshToken()
{
RefreshTokenId = Helper.GetHash(refreshTokenId),
ClientId = clientid,
Subject = context.Ticket.Identity.Name,
IssuedUtc = DateTime.UtcNow,
ExpiresUtc = DateTime.UtcNow.AddMinutes(Convert.ToDouble(refreshTokenLifeTime))
};
context.Ticket.Properties.IssuedUtc = token.IssuedUtc;
context.Ticket.Properties.ExpiresUtc = token.ExpiresUtc;
token.ProtectedTicket = context.SerializeTicket();
var result = await _repo.AddRefreshTokenAsync(token);
if (result)
{
context.SetToken(refreshTokenId);
}
}
}
public async Task ReceiveAsync(AuthenticationTokenReceiveContext context)
{
var allowedOrigin = context.OwinContext.Get<string>("as:clientAllowedOrigin");
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin });
string hashedTokenId = Helper.GetHash(context.Token);
//using (IAuthorizationDataAccess _repo = new AuthRepository())
//{
using (IDataAccessFactory producesIAuthenticateDataAccess = _producesFactoryThatProducesIAuthenticateDataAccess.GetDataAccessFactory()) //using (AuthRepository _repo = new AuthRepository())
{
IAuthorizationDataAccess _repo = producesIAuthenticateDataAccess.GetDataAccess<IAuthorizationDataAccess>();
var refreshToken = await _repo.FindRefreshTokenAsync(hashedTokenId);
if (refreshToken != null)
{
//Get protectedTicket from refreshToken class
context.DeserializeTicket(refreshToken.ProtectedTicket);
var result = await _repo.RemoveRefreshTokenAsync(hashedTokenId);
}
}
}
public void Create(AuthenticationTokenCreateContext context)
{
throw new NotImplementedException();
}
public void Receive(AuthenticationTokenReceiveContext context)
{
throw new NotImplementedException();
}
}
public class SmartCardOAuthAuthorizationProvider : OAuthAuthorizationServerProvider
{
private IDataAccessFactoryFactory _producesFactoryThatProducesIAuthenticateDataAccess;
public SmartCardOAuthAuthorizationProvider(IDataAccessFactoryFactory producesFactoryThatProducesIAuthenticateDataAccess)
{
_producesFactoryThatProducesIAuthenticateDataAccess = producesFactoryThatProducesIAuthenticateDataAccess;
}
public override System.Threading.Tasks.Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var allowedOrigin = context.OwinContext.Get<string>("as:clientAllowedOrigin");
if (allowedOrigin == null) allowedOrigin = "*";
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin });
if (context.UserName != "onlyOneHardCodedUserForSakeOfExploration" && context.Password!="thePassword")
{
context.SetError("invalid_grant", "the user name or password is incorrect");
return Task.FromResult<object>(null); ;
}
ClaimsIdentity identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
identity.AddClaim(new Claim("sub", context.UserName));
identity.AddClaim(new Claim(ClaimTypes.Role, "PostVSDebugBreakModeEnterEventArgs"));
identity.AddClaim(new Claim(DatawareClaimTypes.SmartCardUserId.ToString(), 1.ToString()));
var props = new AuthenticationProperties(new Dictionary<string, string>
{
{
"as:client_id", (context.ClientId == null) ? string.Empty : context.ClientId
},
{
"userName", context.UserName
}
});
var ticket = new AuthenticationTicket(identity, props);
//ticket.Properties.ExpiresUtc = new DateTimeOffset(DateTime.Now.AddDays(2));
context.Validated(ticket);
return Task.FromResult<object>(null);
}
public override System.Threading.Tasks.Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
string clientId = string.Empty;
string clientSecret = string.Empty;
Client client = null;
if (!context.TryGetBasicCredentials(out clientId, out clientSecret))
{
context.TryGetFormCredentials(out clientId, out clientSecret);
}
if (context.ClientId == null)
{
//Remove the comments from the below line context.SetError, and invalidate context
//if you want to force sending clientId/secrects once obtain access tokens.
//context.Validated();
context.SetError("invalid_clientId", "ClientId should be sent.");
return Task.FromResult<object>(null);
}
string[] clientIdClientUnique = context.ClientId.Split(':');
if (clientIdClientUnique == null || clientIdClientUnique.Length <= 1)
{
context.SetError("invalid_client_unique");
return Task.FromResult<object>(null);
}
clientId = clientIdClientUnique[0];
string clientUnique = clientIdClientUnique[1];
using (IDataAccessFactory producesIAuthenticateDataAccess = _producesFactoryThatProducesIAuthenticateDataAccess.GetDataAccessFactory()) //using (AuthRepository _repo = new AuthRepository())
{
IAuthorizationDataAccess _repo = producesIAuthenticateDataAccess.GetDataAccess<IAuthorizationDataAccess>();
client = _repo.FindClient(clientId);//new Client { Active = true, AllowedOrigin = "*", ApplicationType = ApplicationTypes.DesktopClient, ClientId = context.ClientId, Name = "Visual Studio Event Source", RefreshTokenLifeTimeInMinutes = 14400, Secret = Helper.GetHash(clientSecret) };//_repo.FindClient(context.ClientId);
}
if (client == null)
{
//context.SetError("invalid_client_unique");
context.SetError("invalid_clientId", string.Format("Client '{0}' is not registered in the system.", context.ClientId));
return Task.FromResult<object>(null);
}
if (string.IsNullOrWhiteSpace(clientSecret))
{
context.SetError("invalid_clientId", "Client secret should be sent.");
return Task.FromResult<object>(null);
}
else
{
if (client.Secret != Helper.GetHash(clientSecret))
{
context.SetError("invalid_clientId", "Client secret is invalid.");
return Task.FromResult<object>(null);
}
}
if (!client.Active)
{
context.SetError("invalid_clientId", "Client is inactive.");
return Task.FromResult<object>(null);
}
context.OwinContext.Set<string>("as:clientAllowedOrigin", client.AllowedOrigin);
context.OwinContext.Set<string>("as:clientRefreshTokenLifeTime", client.RefreshTokenLifeTimeInMinutes.ToString());
context.Validated();
return Task.FromResult<object>(null);
}
public override Task GrantRefreshToken(OAuthGrantRefreshTokenContext context)
{
var originalClient = context.Ticket.Properties.Dictionary["as:client_id"];
var currentClient = context.ClientId;
if (originalClient != currentClient)
{
context.SetError("invalid_clientId", "Refresh token is issued to a different clientId.");
return Task.FromResult<object>(null);
}
// Change auth ticket for refresh token requests
var newIdentity = new ClaimsIdentity(context.Ticket.Identity);
newIdentity.AddClaim(new Claim("newClaim", "newValue"));
var newTicket = new AuthenticationTicket(newIdentity, context.Ticket.Properties);
context.Validated(newTicket);
return Task.FromResult<object>(null);
}
public override Task TokenEndpoint(OAuthTokenEndpointContext context)
{
foreach (KeyValuePair<string, string> property in context.Properties.Dictionary)
{
context.AdditionalResponseParameters.Add(property.Key, property.Value);
}
return Task.FromResult<object>(null);
}
}
Auch wenn ich eine Anfrage
und die Aktualisierungs-Token abgelaufen sind, keines der oben genannten Klassen \ Methoden getroffen zu werden. Darüber hinaus ist das Aktualisierungstoken, das an die API übergeben wird, nichts anderes als die GUID, die ich in der SmartCardOAuthAuthenticationTokenProvider.CreateAsync
generiert hat, die keine Informationen über Ablauf enthält. Wenn keine der oben genannten Methoden bei der Anforderung von Zugriff durch Aktualisierung angetroffen wird und die Anforderung nichts weitergibt (es sieht nach nichts aus), wenn ein neues Zugriffstoken durch Aktualisierung angefordert wird, wie weiß der Server dann, dass das Aktualisierungstoken abgelaufen ist?
Sieht aus wie Magie für mich.
UPDATE 1 - Hinzufügen Startup Code
public static class OwinStartUpConfig
{
public static void Configure(HttpConfiguration configFromOwinStartup)
{
configFromOwinStartup.MapHttpAttributeRoutes();
configFromOwinStartup.Routes.MapHttpRoute("Default", "{controller}/{id}", new { id = RouteParameter.Optional });
var jsonFormatter = configFromOwinStartup.Formatters.OfType<JsonMediaTypeFormatter>().First();
jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
RegiserDependencies(configFromOwinStartup);
}
public static void RegiserDependencies(HttpConfiguration configFromOwinStartup)
{
string connectionStringForSmartCardDbCntx = System.Configuration.ConfigurationManager.ConnectionStrings["SmartCardDataContext"].ConnectionString;
string projectNameWhenNewProjectCreatedDueToNoMatch = System.Configuration.ConfigurationManager.AppSettings["ProjectNameWhenNewProjectCreatedDueToNoMatch"];
Autofac.ContainerBuilder builderUsedToRegisterDependencies = new Autofac.ContainerBuilder();
builderUsedToRegisterDependencies.RegisterType<DataAccessFactoryFactoryEf>()
.As<IDataAccessFactoryFactory>()
.WithParameter(new TypedParameter(typeof(string), connectionStringForSmartCardDbCntx));
builderUsedToRegisterDependencies.Register(
c =>
new List<IProjectActivityMatch<VSDebugBreakModeEnterActivity>>
{
new MatchVSProjectWithMostRecentActivity<VSDebugBreakModeEnterActivity>(),
new MatchVSSolutionWithMostRecentActivityActivityMatch<VSDebugBreakModeEnterActivity>(),
new MatchMostRecentActivityMatch<VSDebugBreakModeEnterActivity>(),
new MatchToNewProjectActivityMatch<VSDebugBreakModeEnterActivity>(projectNameWhenNewProjectCreatedDueToNoMatch)
}
).As<IEnumerable<IProjectActivityMatch<VSDebugBreakModeEnterActivity>>>();
builderUsedToRegisterDependencies
.RegisterType<MatchDontGiveUpActivityMatch<VSDebugBreakModeEnterActivity>>()
//.WithParameter(Autofac.Core.ResolvedParameter.ForNamed<IEnumerable<IProjectActivityMatch<VSDebugBreakModeEnterActivity>>>("VSDebugBreakModeEnterActivityMatchers"))
.As<IProjectActivityMatch<VSDebugBreakModeEnterActivity>>();
builderUsedToRegisterDependencies
.RegisterType<VSDebugBreakModeEnterEventArgsEventSaver>()
.Named<ISaveVisualStudioEvents>("VSDebugBreakModeEnterSaver");
builderUsedToRegisterDependencies
.RegisterType<VSDebugBreakModeEnterEventArgsController>()
.WithParameter(Autofac.Core.ResolvedParameter.ForNamed<ISaveVisualStudioEvents>("VSDebugBreakModeEnterSaver"));
var container = builderUsedToRegisterDependencies.Build();
configFromOwinStartup.DependencyResolver = new AutofacWebApiDependencyResolver(container);
}
}
public static class OAuthStartupConfig
{
internal static void Configure(IAppBuilder app)
{
OAuthAuthorizationServerOptions oAuthServerOptions = new OAuthAuthorizationServerOptions
{
AllowInsecureHttp = false,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1), //TimeSpan.FromDays(1),
Provider = new SmartCardOAuthAuthorizationProvider(new AuthorizationDataAccessFactoryFactory()),
RefreshTokenProvider = new SmartCardOAuthAuthenticationTokenProvider(new AuthorizationDataAccessFactoryFactory())
};
app.UseOAuthAuthorizationServer(oAuthServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
}
}
UPDATE - In Reaktion auf die Kommentare
Das Zugriffstoken in der obigen Antwort Ansprüche Informationen enthält und den Ablauf Informationen für den Zugriffstoken . All diese Informationen werden vom Server in das oben genannte Zugriffstoken serialisiert. So kann ich sehen, wie der Ablauf des Zugriffstokens überprüft wird. Was ist jedoch mit dem Aktualisierungstoken? Wenn eine Anforderung für einen Zugriffstoken wird mit den Aktualisierungs-Token machen:
In der Anfrage über den Aktualisierungs-Token gut ist und so die Anforderung für die Zugriffstoken durch Aktualisierungs-Token gewährt wird, aber wenn der Auffrischungs Token abgelaufen sind, wie würde OAuthAuthorizationServerProvider das Refresh-Token auf Ablauf überprüfen?
Auch hier habe ich überprüft, ob der oben bereitgestellte Code tatsächlich nach abgelaufenen Refresh-Tokens sucht und kein Zugriffstoken gewährt, wenn das Refresh-Token abgelaufen ist, aber WIE weiß es? Ich habe nichts geschrieben, um den Ablauf des Aktualisierungs-Tokens in meiner abgeleiteten Klasse OAuthAuthorizationServerProvider zu überprüfen. Also dann wie ????
UPDATE
Die Magie geschieht in der ReceiveAsync Methode der IAuthenticationTokenProvider Umsetzung.
public async Task ReceiveAsync(AuthenticationTokenReceiveContext context)
{
var allowedOrigin = context.OwinContext.Get<string>("as:clientAllowedOrigin");
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin });
string hashedTokenId = Helper.GetHash(context.Token);
//using (IAuthorizationDataAccess _repo = new AuthRepository())
//{
using (IDataAccessFactory producesIAuthenticateDataAccess = _producesFactoryThatProducesIAuthenticateDataAccess.GetDataAccessFactory()) //using (AuthRepository _repo = new AuthRepository())
{
IAuthorizationDataAccess _repo = producesIAuthenticateDataAccess.GetDataAccess<IAuthorizationDataAccess>();
var refreshToken = await _repo.FindRefreshTokenAsync(hashedTokenId);
if (refreshToken != null)
{
//Get protectedTicket from refreshToken class
context.DeserializeTicket(refreshToken.ProtectedTicket);
var result = await _repo.RemoveRefreshTokenAsync(hashedTokenId);
}
}
}
Besonders die Linie context.DeserializeTicket(refreshToken.ProtectedTicket);
ist die Magie. Dadurch wird die context.Ticket-Eigenschaft festgelegt. Nachdem die Methode ReceiveAsync abgeschlossen ist. Keine Notwendigkeit, manuell etwas zu überprüfen OWIN, irgendwo hinter den Kulissen, weiß, dass das Ticket abgelaufen ist.
Können Sie den Code anzeigen, in dem Sie Ihren Provider mit den OAuthServerOptions verbinden? Ich vermute, dass Sie beim Erstellen der Serveroptionen nicht auf einen Aktualisierungstokenanbieter zeigen. –