2013-08-15 22 views
12

Utworzono serwer autoryzacji OAuth2 przy użyciu DotNetOpenAuth, który działa poprawnie - używam przepływu hasła właściciela zasobów i pomyślnie wymieniam dane logowania użytkownika na token dostępu.Jak mogę autoryzować dostęp do zasobów usługi ServiceStack przy użyciu tokenów dostępu OAuth2 za pośrednictwem DotNetOpenAuth?

Chcę teraz użyć tego tokena dostępu do pobierania danych z bezpiecznych punktów końcowych w interfejsie API usługi ServiceStack i nie mogę się dowiedzieć, jak to zrobić. Zbadałem dostawców Facebooka, Google itp. Dołączonych do ServiceStack, ale nie jest jasne, czy powinienem stosować ten sam schemat, czy nie.

Co staram się osiągnąć (myślę!) Jest

  1. OAuth klient (mój app) prosi właściciel zasobu ('Katarzyna Smith') o poświadczenia
  2. Client przesyła żądanie do serwera autoryzacji , otrzymuje token dostępu , który otrzymuje
  3. Klient żąda bezpiecznego zasób z serwera zasobów (GET /users/csmith/photos)
    • token dostępu jest zawarty w nagłówku HTTP, na przykład Authorization: Bearer 1234abcd...
  4. Serwer zasób odszyfrowuje token dostępu do weryfikacji tożsamości właściciela zasobów
  5. Serwer zasób sprawdza, czy właściciel zasób ma dostępu do żądanego zasobu
  6. Serwer zasobów zwraca wartość zasób do klienta

kroki 1 i 2 pracują, ale nie mogę dowiedzieć się, jak zintegrować kod zasobów serwera DotNetOpenAuth z ramami autoryzacji ServiceStack.

Czy istnieje jakiś przykład tego, jak to osiągnąć? Znalazłem podobny post StackOverflow na How to build secured api using ServiceStack as resource server with OAuth2.0?, ale nie jest to kompletne rozwiązanie i wydaje się nie używać modelu dostawcy autoryzacji ServiceStack.

EDIT: Trochę więcej szczegółów. Tutaj są dwie różne aplikacje internetowe. Jednym z nich jest serwer autoryzacji/autoryzacji - nie ma w nim żadnych danych klienta (tzn. Nie ma interfejsu API danych), ale udostępnia metodę/oauth/token, która akceptuje nazwę użytkownika/hasło i zwraca token dostępu OAuth2 i odświeżenie tokena, a także zapewnia możliwość odświeżania tokena. Jest on zbudowany na ASP.NET MVC, ponieważ jest prawie identyczny z próbką AuthorizationServer dołączoną do DotNetOpenAuth. Może to zostać zastąpione później, ale na razie jest to ASP.NET MVC.

Dla rzeczywistego API danych używam ServiceStack, ponieważ uważam, że jest znacznie lepszy niż WebAPI lub MVC do ujawniania usług danych ReST.

Dlatego w poniższym przykładzie:

sequence diagram

Client to aplikacja działa na lokalnym komputerze użytkownika, serwer Auth jest ASP.NET MVC + DotNetOpenAuth i Serwer zasobów to ServiceStack

Specjalny fragment kodu DotNetOpenAuth, który jest wymagany, to:

// scopes is the specific OAuth2 scope associated with the current API call. 
var scopes = new string[] { "some_scope", "some_other_scope" } 

var analyzer = new StandardAccessTokenAnalyzer(authServerPublicKey, resourceServerPrivateKey); 
var resourceServer = new DotNetOpenAuth.OAuth2.ResourceServer(analyzer); 
var wrappedRequest = System.Web.HttpRequestWrapper(HttpContext.Current.Request); 
var principal = resourceServer.GetPrincipal(wrappedRequest, scopes); 

if (principal != null) { 
    // We've verified that the OAuth2 access token grants this principal 
    // access to the requested scope. 
} 

Zakładając, że jestem na dobrej drodze, potrzebuję uruchomić ten kod gdzieś w potoku żądania ServiceStack, aby sprawdzić, czy nagłówek Authorization w żądaniu API reprezentuje prawidłowego zleceniodawcę, który ma przyznano dostęp do wnioskowanego zakresu.

Zaczynam myśleć najbardziej logicznym miejscem do realizacji tego jest w atrybutu niestandardowego, że mogę używać do dekoracji swoje implementacje usług ServiceStack:

using ServiceStack.ServiceInterface; 
using SpotAuth.Common.ServiceModel; 

namespace SpotAuth.ResourceServer.Services { 
    [RequireScope("hello")] 
    public class HelloService : Service { 
     public object Any(Hello request) { 
      return new HelloResponse { Result = "Hello, " + request.Name }; 
     } 
    } 
} 

Takie podejście pozwoliłoby również określenie zakresu (ów) wymagane dla każdej metody usługi. Wydaje się to jednak działać wbrew zasadzie "wtykania" za OAuth2 i haczykom rozszerzalności wbudowanym w model AuthProvider ServiceStack.

Innymi słowy - Martwię jestem walić w paznokci z buta, bo nie mogę znaleźć młotka ...

+0

Czy [dostawcy uwierzytelnień OpenId dla ServiceStack] (https://www.nuget.org/packages/ServiceStack.Authentication.OpenId) pasują do Twoich potrzeb? Oto dokumentacja - [Obsługa uwierzytelniania OpenId 2.0] (https://github.com/ServiceStack/ServiceStack/wiki/OpenId) –

+0

Ten pakiet może dostarczyć użytecznego przykładu kodu: [Skonfiguruj backend ServiceStack, aby akceptował tokeny na Okaziciela OAuth2] (http : //www.nuget.org/packages/Auth10.ServiceStack/) –

+0

Jeśli chodzi o autoryzację, zalecana lektura dla wbudowanych ról i wsparcia uprawnień znajduje się tutaj: http://stackoverflow.com/questions/12095094/servicestack-web- services-security/12096813 # 12096813 i tutaj jest pełniejszy przykład dodania niestandardowego dostawcy OAuth niż ten, który łączysz: http://davetimmins.wordpress.com/2013/04/12/using-oauth-with-arcgis-online -i-servicestack/- masz rację. Nie widzę przykładów, które całkowicie integrują te dwie autoryzacje OAuth wraz z autoryzacją ról i uprawnień. –

Odpowiedz

6

Aktualizacja W dalszej refleksji, początkowy myśl , stworzenie atrybutu RequiredScope byłoby czystszym sposobem. Dodanie go do rurociągu ServiceStack jest tak proste jak dodanie interfejsu IHasRequestFilter, wdrażanie filtr niestandardowy żądania, co zostało udokumentowane tutaj: https://github.com/ServiceStack/ServiceStack/wiki/Filter-attributes

public class RequireScopeAttribute : Attribute, IHasRequestFilter { 
    public void RequireScope(IHttpRequest req, IHttpResponse res, object requestDto) 
    { 
     //This code is executed before the service 
     //Close the request if user lacks required scope 
    } 

    ... 
} 

Następnie udekorować swój DTO użytkownika lub usług, jak już wskazano:

using ServiceStack.ServiceInterface; 
using SpotAuth.Common.ServiceModel; 

namespace SpotAuth.ResourceServer.Services { 
    [RequireScope("hello")] 
    public class HelloService : Service { 
     public object Any(Hello request) { 
      return new HelloResponse { Result = "Hello, " + request.Name }; 
     } 
    } 
} 

Twój niestandardowy filtr RequireScope będzie prawie identyczny z ServiceStack's RequiredRoleAttribute implementation., więc używaj go jako punktu wyjścia do kodowania.

Alternatywnie, możesz odwzorować zakres na uprawnienia. Następnie udekorować swój Dto lub Odpowiednio usługi (see SS wiki szczegóły) na przykład:

[Authenticate] 
[RequiredPermission("Hello")] 
    public class HelloService : Service { 
     public object Any(Hello request) { 
      return new HelloResponse { Result = "Hello, " + request.Name }; 
     } 
    } 

Normalnie ServiceStack wywołuje HasPermission metoda bool (zezwolenie string) w IAuthSession. Ta metoda sprawdza, czy uprawnienia listy listy w IAuthSession zawierają wymagane uprawnienia, więc w niestandardowej IAuthSession można zastąpić HasPermission i sprawdzić tam zakresy OAuth2.

+0

Właściwie wygląda na to, że chcesz zastąpić HasPermission, ale ta sama zasada obowiązuje, kod znajduje się w implementacji IAuthSession. –

+0

Poprawiono moją odpowiedź, aby zalecić niestandardowy filtr żądania jako lepsze rozwiązanie niż tworzenie niestandardowej IAuthSession i nadpisywanie HasPermission() –

+0

Zaakceptowane za skierowanie mnie we właściwym kierunku - dzięki! - ale zobacz moją własną odpowiedź na pełne wdrożenie i wyjaśnienia. –

8

OK, po partii w przechodzeniu poszczególnych bibliotek z debuggera, myślę, że robisz to tak: https://github.com/dylanbeattie/OAuthStack

Są dwa główne punkty integracyjne. Po pierwsze, atrybut niestandardowy filtr, który jest używany na serwerze, aby ozdobić punkty końcowe zasobów, które powinny być zabezpieczone zezwolenia OAuth2:

/// <summary>Restrict this service to clients with a valid OAuth2 access 
/// token granting access to the specified scopes.</summary> 
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true)] 
public class RequireOAuth2ScopeAttribute : RequestFilterAttribute { 
    private readonly string[] oauth2Scopes; 
    public RequireOAuth2ScopeAttribute(params string[] oauth2Scopes) { 
     this.oauth2Scopes = oauth2Scopes; 
    } 

    public override void Execute(IHttpRequest request, IHttpResponse response, object requestDto) { 
     try { 
      var authServerKeys = AppHostBase.Instance.Container.ResolveNamed<ICryptoKeyPair>("authServer"); 
      var dataServerKeys = AppHostBase.Instance.Container.ResolveNamed<ICryptoKeyPair>("dataServer"); 
      var tokenAnalyzer = new StandardAccessTokenAnalyzer(authServerKeys.PublicSigningKey, dataServerKeys.PrivateEncryptionKey); 
      var oauth2ResourceServer = new DotNetOpenAuth.OAuth2.ResourceServer(tokenAnalyzer); 
      var wrappedRequest = new HttpRequestWrapper((HttpRequest)request.OriginalRequest); 
      HttpContext.Current.User = oauth2ResourceServer.GetPrincipal(wrappedRequest, oauth2Scopes); 
     } catch (ProtocolFaultResponseException x) { 
      // see the GitHub project for detailed error-handling code 
      throw; 
     } 
    } 
} 

Po drugie, jest to, w jaki sposób zaczepić do klienta rurociągu ServiceStack HTTP i używać DotNetOpenAuth aby dodać OAuth2 Authorization: Bearer {key} tokena do żądania wychodzącego:

// Create the ServiceStack API client and the request DTO 
var apiClient = new JsonServiceClient("http://api.mysite.com/"); 
var apiRequestDto = new Shortlists { Name = "dylan" }; 

// Wire up the ServiceStack client filter so that DotNetOpenAuth can 
// add the authorization header before the request is sent 
// to the API server 
apiClient.LocalHttpWebRequestFilter = request => { 
    // This is the magic line that makes all the client-side magic work :) 
    ClientBase.AuthorizeRequest(request, accessTokenTextBox.Text); 
} 

// Send the API request and dump the response to our output TextBox 
var helloResponseDto = apiClient.Get(apiRequestDto); 

Console.WriteLine(helloResponseDto.Result); 

Autoryzowany wnioski uda; Żądania z brakującym tokenem, wygasłym tokenem lub niewystarczającym zasięgiem podniosą wartość tego problemu, ale wygląda na to, że działa całkiem nieźle. Chciałbym poznać opinie wszystkich, którzy znają ServiceStack lub DotNetOpenAuth lepiej niż ja.

Powiązane problemy