2014-06-21 13 views
14

Próbuję wprowadzić autoryzację tokena na okaziciela OWIN i na podstawie this article. Jest jednak jeszcze jedna dodatkowa informacja, której potrzebuję w torze na okaziciela, którego nie wiem, jak ją wprowadzić.Dodawanie dodatkowej logiki do autoryzacji na okaziciela

W mojej aplikacji muszę wydedukować informacje o użytkowniku tokena właściciela (np. Identyfikator użytkownika). Jest to ważne, ponieważ nie chcę, aby upoważniony użytkownik mógł działać jako inny użytkownik. Czy to jest wykonalne? Czy to nawet właściwe podejście? Jeśli identyfikator użytkownika to GUID, byłoby to proste. W tym przypadku jest liczbą całkowitą. Autoryzowany użytkownik może potencjalnie podszyć się pod inną osobę tylko przez zgadywanie/brutalną siłę, co jest niedopuszczalne.

Patrząc na ten kod:

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

Myślę, że jest możliwe, aby zastąpić autoryzacji/uwierzytelnienia, aby pomieścić co potrzebne?

+0

można użyć następujących czynności, aby uzyskać identyfikator użytkownika od użytkownika http://stackoverflow.com/a/19506296/299327. –

Odpowiedz

15

Wygląda na to, że w Twoim kodzie brakuje czegoś.
Nie sprawdzasz poprawności swojego klienta.

Powinieneś zaimplementować ValidateClientAuthentication i sprawdzić dane uwierzytelniające swojego klienta.

To co mam zrobić:

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

Dobry artykuł pełen szczegółów można znaleźć here. W tej serii blog można znaleźć jeszcze lepsze wyjaśnienie.

UPDATE:

zrobiłem kilka kopanie i webstuff ma rację.

Aby zdać errorDescription do klienta musimy Odrzucone przed ustawiamy błąd z SetError:

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

lub możemy przedłużyć to przepuszczanie zserializowaną obiekt json w opisie:

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

Z klientem javascript/jQuery możemy deserializować odpowiedź tekstową i przeczytać rozszerzenie ed wiadomość:

$.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

Przeczytałem ten artykuł, a jedyną rzeczą, której nie rozumiem, jest to, w jaki sposób autor wymyślił: Podstawowy NDJmZjVkYWQzYzI3NGM5N2EzYTdjM2Q0NGI2N2JiNDI6Y2xpZW50MTIzNDU2. Potem spojrzałem na link github, który autor opublikował, i wreszcie zobaczyłem stronę klienta. Oauth2 magia na ratunek! Dzięki leftyx – Echiban

+1

@Echiban: Jeśli potrzebujesz więcej informacji, w mojej odpowiedzi jest nowy link. Blog Taisser o owin i web api jest świetny i pełen przydatnych informacji. Twoje zdrowie. – LeftyX

+0

jeśli muszę przekazać 2 wartości z powrotem, czy mogę dwukrotnie wywołać SetError? – user230910

9

Na marginesie, jeśli chcesz ustawić niestandardowy komunikat o błędzie, musisz zamienić kolejność context.Rejected i context.SetError.

// Summary: 
    //  Marks this context as not validated by the application. IsValidated and HasError 
    //  become false as a result of calling. 
    public virtual void Rejected(); 

Jeśli postawisz context.Rejected po context.SetError wówczas właściwość context.HasError zostaną przywrócone dlatego fałszywego prawidłowy sposób z niego korzystać to:

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

Cholera, że ​​to rzeczywiście wystarczyło. Jestem zaskoczony, jak Copy-Paste rozprzestrzenia się w społeczności .NET. Dziękuję za twoją unikalną odpowiedź. –

+0

Czy możemy wysłać błąd 401 lub 403? Jedyny kod błędu, jaki widzę, to 400 – InTheWorldOfCodingApplications

0

Wystarczy dodać na odpowiedź LeftyX, oto sposób możesz całkowicie kontrolować odpowiedź wysyłaną do klienta po odrzuceniu kontekstu. Zwróć uwagę na komentarze do kodu.

Based on Greg P's original answer, z pewnymi modyfikacjami

Krok 1: Tworzenie klasy, która będzie działać jako middleware

using AppFunc = System.Func<System.Collections.Generic.IDictionary<string, System.Object>, 
System.Threading.Tasks.Task>; 

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

Krok 2 : Utwórz klasę rozszerzeń (można pominąć) .

Ten krok jest opcjonalny, można go zmodyfikować, aby zaakceptować opcje, które można przekazać do oprogramowania pośredniego.

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

Krok 3: Modyfikacja GrantResourceOwnerCredentials metody w implementacji OAuthAuthorizationServerProvider

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

Krok 4: Za pomocą tego oprogramowania pośredniczącego w klasie startowej

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

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

    //rest of your code goes here.... 
} 
Powiązane problemy