2014-06-21 14 views
14

Ich versuche OWIN Bearer Token-Autorisierung zu implementieren, und basiert auf this article. Es gibt jedoch noch eine weitere Information, die ich in einem Bearer-Token brauche, von der ich nicht weiß, wie ich sie implementieren soll.Hinzufügen zusätzlicher Logik zur Bearer-Autorisierung

In meiner Anwendung muss ich aus dem Bearer-Token Benutzerinformationen ableiten (sagen Benutzer-ID). Dies ist wichtig, weil ich nicht möchte, dass ein autorisierter Benutzer als ein anderer Benutzer agieren kann. Ist das machbar? Ist es überhaupt der richtige Ansatz? Wenn die Benutzer-ID eine GUID ist, wäre dies einfach. Es ist in diesem Fall eine ganze Zahl. Ein autorisierter Benutzer kann sich möglicherweise nur durch Erraten/Brute Force als ein anderer ausgeben, was nicht akzeptabel ist.

an diesem Code suchen:

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 außer Kraft zu setzen aufzunehmen, was ich brauche?

+0

Sie können die Verwendung nach dem bekommen Benutzer-ID von einem Benutzer http://stackoverflow.com/a/19506296/299327. –

Antwort

15

Es scheint, dass in Ihrem Code etwas fehlt.
Sie validieren Ihren Client nicht.

Sie sollten ValidateClientAuthentication implementieren und überprüfen Sie die Anmeldeinformationen Ihres Clients dort. Diese

ist, was ich tue:

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(); 
     } 
    } 

Ein guter Artikel voller Details here gefunden werden kann. Eine noch bessere Erklärung findet sich in dieser blog Serie.

UPDATE:

habe ich einige graben und webstuff ist richtig.

Um errorDescription an den Client übergeben wir Zurückgewiesen müssen, bevor wir den Fehler mit SetError eingestellt:

context.Rejected(); 
context.SetError("invalid_client", "The information provided are not valid !"); 
return; 

oder wir können sie erstrecken, um eine serialisierte json Objekt in der Beschreibung vorbei:

context.Rejected(); 
context.SetError("invalid_client", Newtonsoft.Json.JsonConvert.SerializeObject(new { result = false, message = "The information provided are not valid !" })); 
return; 

enter image description here

Mit einem javascript/jQuery Client konnten wir die Textantwort deserialisieren und die Erweiterung lesen ed Nachricht:

$.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); 
      } 
    } 
}); 
+0

ich gelesen, dass Artikel, und das einzige, was ich nicht verstehe, ist, wie in der Welt mit Authorization kam der Autor hat: Grund NDJmZjVkYWQzYzI3NGM5N2EzYTdjM2Q0NGI2N2JiNDI6Y2xpZW50MTIzNDU2. Ich schaute dann auf den GitHub-Link, den der Autor gepostet hatte, und sah schließlich die Client-Seite. Oauth2 Magie zur Rettung! Dank leftyx – Echiban

+1

@Echiban: Wenn Sie mehr Informationen benötigen, gibt es einen neuen Link in meiner Antwort. Taissers Blog über owin und web api ist großartig und voller nützlicher Informationen. Prost. – LeftyX

+0

wenn ich brauche zwei Werte zurück zu passieren, kann ich SetError zweimal anrufen? – user230910

9

Auf einer Seite beachten, wenn Sie Nachricht einen benutzerdefinierten Fehler setzen möchten, müssen Sie die Reihenfolge der context.Rejected und context.SetError tauschen müssen.

// 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 nach context.SetError legen dann die Eigenschaft context.HasError wird falsch zurückgesetzt werden deshalb der richtige Weg, es zu benutzen ist:

// Client could not be validated. 
    context.Rejected(); 
    context.SetError("invalid_client", "Client credentials are invalid."); 
+1

Verdammt, das hat tatsächlich den Trick gemacht. Ich bin überrascht, wie die Copy-Paste auf die .NET-Community verteilt ist. Danke für Ihre einzigartige Antwort. –

+0

Können wir einen Fehler 401 oder 403 senden? Der einzige Fehlercode, den ich sehe, ist 400 – InTheWorldOfCodingApplications

0

einfach hinzufügen auf LeftyX Antwort, hier ist, wie Sie können die Antwort, die an den Client gesendet wird, vollständig kontrollieren, sobald der Kontext abgelehnt wurde. Beachten Sie die Code-Kommentare.

Based on Greg P's original answer, mit einigen Modifikationen

Schritt 1: Erstellen Sie eine Klasse, die als Middleware

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"); 
     } 
    } 
} 

Schritt 2 handeln : Erstellen Sie die Erweiterungsklasse (kann weggelassen werden) .

können Dieser Schritt ist optional, geändert werden 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>(); 
    } 
} 

Schritt 3: Ändern 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; 
     } 
    } 

Schritt 4: Diese Middleware in der Klasse Start verwenden

public void Configuration(IAppBuilder app) 
{ 
    app.UseResponseMiddleware(); 

    //configure the authentication server provider 
    ConfigureOAuth(app); 

    //rest of your code goes here.... 
} 
Verwandte Themen