Ich versuche, die OWIN-Träger-Token-Autorisierung zu implementieren und basiert auf diesem Artikel . Es gibt jedoch eine zusätzliche Information, die ich als Trägermarke benötige, die ich nicht umsetzen kann.
In meiner Anwendung muss ich aus dem Träger-Token Benutzerinformationen ableiten (z. B. userid). Dies ist wichtig, weil ich nicht will, dass ein berechtigter Benutzer als anderer Benutzer fungiert. Ist das machbar? Ist es überhaupt der richtige Ansatz? Wenn die Benutzer-ID eine Guid ist, wäre dies einfach. In diesem Fall ist es eine ganze Zahl. Ein autorisierter Benutzer kann sich möglicherweise nur durch Erraten/Brute Force als unpersönlich ausgeben, was inakzeptabel ist.
Betrachten Sie diesen Code:
public void ConfigureOAuth(IAppBuilder app)
{
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new SimpleAuthorizationServerProvider()
};
// Token Generation
app.UseOAuthAuthorizationServer(OAuthServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
}
public class SimpleAuthorizationServerProvider : OAuthAuthorizationServerProvider
{
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
using (AuthRepository _repo = new AuthRepository())
{
IdentityUser user = await _repo.FindUser(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
}
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim("sub", context.UserName));
identity.AddClaim(new Claim("role", "user"));
context.Validated(identity);
}
}
Ich würde denken, dass es möglich ist, die Autorisierung/Authentifizierung zu überschreiben, um dem zu entsprechen, was ich brauche.
Es scheint, dass etwas in Ihrem Code fehlt.
Sie überprüfen Ihren Kunden nicht.
Sie sollten ValidateClientAuthentication implementieren und die Anmeldeinformationen Ihres Clients dort überprüfen.
Das ist was ich mache:
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
string clientId = string.Empty;
string clientSecret = string.Empty;
if (!context.TryGetBasicCredentials(out clientId, out clientSecret))
{
context.SetError("invalid_client", "Client credentials could not be retrieved through the Authorization header.");
context.Rejected();
return;
}
ApplicationDatabaseContext dbContext = context.OwinContext.Get<ApplicationDatabaseContext>();
ApplicationUserManager userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
if (dbContext == null)
{
context.SetError("server_error");
context.Rejected();
return;
}
try
{
AppClient client = await dbContext
.Clients
.FirstOrDefaultAsync(clientEntity => clientEntity.Id == clientId);
if (client != null && userManager.PasswordHasher.VerifyHashedPassword(client.ClientSecretHash, clientSecret) == PasswordVerificationResult.Success)
{
// Client has been verified.
context.OwinContext.Set<AppClient>("oauth:client", client);
context.Validated(clientId);
}
else
{
// Client could not be validated.
context.SetError("invalid_client", "Client credentials are invalid.");
context.Rejected();
}
}
catch (Exception ex)
{
string errorMessage = ex.Message;
context.SetError("server_error");
context.Rejected();
}
}
Einen guten Artikel mit vielen Details finden Sie hier .
Eine noch bessere Erklärung finden Sie in dieser blog -Reihe.
UPDATE:
Ich habe ein wenig gegraben und webstuff ist richtig.
Um errorDescription
an den Client zu übergeben, müssen wir Ablehnen, bevor wir den Fehler mit SetError
setzen:
context.Rejected();
context.SetError("invalid_client", "The information provided are not valid !");
return;
oder wir können es in der Beschreibung durch ein serialisiertes Json-Objekt erweitern:
context.Rejected();
context.SetError("invalid_client", Newtonsoft.Json.JsonConvert.SerializeObject(new { result = false, message = "The information provided are not valid !" }));
return;
Mit einem javascript/jQuery
-Client könnten wir die Textantwort deserialisieren und die erweiterte Nachricht lesen:
$.ajax({
type: 'POST',
url: '<myAuthorizationServer>',
data: { username: 'John', password: 'Smith', grant_type: 'password' },
dataType: "json",
contentType: 'application/x-www-form-urlencoded; charset=utf-8',
xhrFields: {
withCredentials: true
},
headers: {
'Authorization': 'Basic ' + authorizationBasic
},
error: function (req, status, error) {
if (req.responseJSON && req.responseJSON.error_description)
{
var error = $.parseJSON(req.responseJSON.error_description);
alert(error.message);
}
}
});
Wenn Sie eine benutzerdefinierte Fehlermeldung festlegen möchten, müssen Sie die Reihenfolge von context.Rejected
und context.SetError
vertauschen.
// Summary:
// Marks this context as not validated by the application. IsValidated and HasError
// become false as a result of calling.
public virtual void Rejected();
Wenn Sie context.Rejected
hinter context.SetError
platzieren, wird die Eigenschaft context.HasError
auf false zurückgesetzt. Daher lautet die korrekte Verwendung:
// Client could not be validated.
context.Rejected();
context.SetError("invalid_client", "Client credentials are invalid.");
Um die Antwort von LeftyX zu ergänzen, können Sie die Antwort, die an den Client gesendet wird, vollständig steuern, sobald der Kontext abgelehnt wird. Beachten Sie die Code-Kommentare.
Basierend auf der ursprünglichen Antwort von Greg P , mit einigen Modifikationen
Step1: Erstellen Sie eine Klasse, die als Middleware fungiert.
using AppFunc = System.Func<System.Collections.Generic.IDictionary<string, System.Object>,
System.Threading.Tasks.Task>;
namespace SignOnAPI.Middleware.ResponseMiddleware {
public class ResponseMiddleware
{
AppFunc _next;
ResponseMiddlewareOptions _options;
public ResponseMiddleware(AppFunc nex, ResponseMiddlewareOptions options)
{
_next = next;
}
public async Task Invoke(IDictionary<string, object> environment)
{
var context = new OwinContext(environment);
await _next(environment);
if (context.Response.StatusCode == 400 && context.Response.Headers.ContainsKey("Change_Status_Code"))
{
//read the status code sent in the response
var headerValues = context.Response.Headers.GetValues("Change_Status_Code");
//replace the original status code with the new one
context.Response.StatusCode = Convert.ToInt16(headerValues.FirstOrDefault());
//remove the unnecessary header flag
context.Response.Headers.Remove("Change_Status_Code");
}
}
}
Step2: Erstellen Sie die Erweiterungsklasse (kann weggelassen werden).
Dieser Schritt ist optional und kann geändert werden, um Optionen zu akzeptieren, die an die Middleware übergeben werden können.
public static class ResponseMiddlewareExtensions
{
//method name that will be used in the startup class, add additional parameter to accept middleware options if necessary
public static void UseResponseMiddleware(this IAppBuilder app)
{
app.Use<ResponseMiddleware>();
}
}
Schritt3: Ändern Sie die GrantResourceOwnerCredentials
-Methode in Ihrer OAuthAuthorizationServerProvider
-Implementierung.
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
if (<logic to validate username and password>)
{
//first reject the context, to signify that the client is not valid
context.Rejected();
//set the error message
context.SetError("invalid_username_or_password", "Invalid userName or password" );
//add a new key in the header along with the statusCode you'd like to return
context.Response.Headers.Add("Change_Status_Code", new[] { ((int)HttpStatusCode.Unauthorized).ToString() });
return;
}
}
Step4: Verwenden Sie diese Middleware in der Startklasse.
public void Configuration(IAppBuilder app)
{
app.UseResponseMiddleware();
//configure the authentication server provider
ConfigureOAuth(app);
//rest of your code goes here....
}