2013-08-15 6 views
12

Ho creato un server di autorizzazione OAuth2 utilizzando DotNetOpenAuth, che funziona correttamente: sto utilizzando il flusso della password del proprietario della risorsa e lo scambio delle credenziali dell'utente con un token di accesso.Come autorizzo l'accesso alle risorse di ServiceStack usando i token di accesso OAuth2 tramite DotNetOpenAuth?

Ora desidero utilizzare quel token di accesso per recuperare i dati da endpoint sicuri in un'API ServiceStack e non riesco a capire come farlo. Ho esaminato i fornitori di Facebook, Google, ecc. Inclusi in ServiceStack ma non è chiaro se dovrei seguire lo stesso schema o no.

Quello che sto cercando di realizzare (credo!) È

  1. OAuth cliente (la mia app) chiede Risorsa ('Catherine Smith') per le credenziali
  2. client invia la richiesta al server di autorizzazione , riceve un token di accesso
  3. Cliente richiede una sicura risorsa dal server risorsa (GET /users/csmith/photos)
    • Il token di accesso è incluso in un'intestazione HTTP, per esempio Authorization: Bearer 1234abcd...
  4. Il server risorsa decodifica il token di accesso per verificare l'identità del titolare risorsa
  5. Il server risorsa verifica che il proprietario risorsa ha accesso agli elementi risorsa richiesta
  6. Il server di risorse restituisce risorsa al cliente

punti 1 e 2 stanno lavorando, ma non riesco a capire come integrare il codice del server risorsa DotNetOpenAuth con il framework di autorizzazione ServiceStack.

Esiste un esempio da qualche parte su come ottenerlo? Ho trovato un post StackOverflow simile a How to build secured api using ServiceStack as resource server with OAuth2.0? ma non è una soluzione completa e non sembra utilizzare il modello del provider di autorizzazioni ServiceStack.

MODIFICA: Un po 'più in dettaglio. Ci sono due diverse app Web in gioco qui. Uno è il server di autenticazione/autorizzazione - questo non ospita alcun dato cliente (cioè nessuna API dati), ma espone il metodo/oauth/token che accetterà un nome utente/password e restituirà un token di accesso OAuth2 e un token di aggiornamento, e anche fornisce funzionalità di aggiornamento dei token. Questo è basato su ASP.NET MVC perché è quasi identico all'esempio AuthorizationServer incluso con DotNetOpenAuth. Questo potrebbe essere sostituito in seguito, ma per ora è ASP.NET MVC.

Per l'effettiva API di dati, sto utilizzando ServiceStack perché lo trovo molto meglio di WebAPI o MVC per l'esposizione dei servizi di dati di ReSTful.

Così nel seguente esempio:

sequence diagram

il client è un'applicazione desktop in esecuzione sul computer locale di un utente, il server Auth è ASP.NET MVC + DotNetOpenAuth, e il Il server di risorse è ServiceStack

Il particolare frammento di codice DotNetOpenAuth richiesto è:

Quindi, supponendo che sia sulla strada giusta, quello che devo fare è eseguire quel codice da qualche parte nella pipeline di richieste di ServiceStack, per verificare che l'intestazione Autorizzazione nella richiesta API rappresenti un principal valido che ha concesso l'accesso all'ambito richiesto.

sto iniziando a pensare che il posto più logico per attuare questa si trova in un attributo personalizzato che uso per decorare la mia ServiceStack implementazioni del servizio:

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

Questo approccio permetterà anche specificare il campo di applicazione (s) richiesto per ciascun metodo di servizio. Tuttavia, ciò sembra essere piuttosto contrario al principio "collegabile" dietro OAuth2 e ai ganci di estensibilità integrati nel modello AuthProvider di ServiceStack.

In altre parole - Sono preoccupato che sto sbattendo in un chiodo con una scarpa perché non riesco a trovare un martello ...

+0

Fa [OpenId Auth Provider per ServiceStack] (https://www.nuget.org/packages/ServiceStack.Authentication.OpenId) adatto alle tue esigenze? Ecco la documentazione - [OpenId 2.0 Authentication Support] (https://github.com/ServiceStack/ServiceStack/wiki/OpenId) –

+0

Questo pacchetto potrebbe fornire un utile esempio di codice: [Configurare un backend ServiceStack per accettare token OAuth2 Bearer] (http : //www.nuget.org/packages/Auth10.ServiceStack/) –

+0

Per quanto riguarda l'autorizzazione, la lettura consigliata per i ruoli incorporati e il supporto delle autorizzazioni è disponibile qui: http://stackoverflow.com/questions/12095094/servicestack-web- services-security/12096813 # 12096813 ed ecco un esempio più completo di aggiunta di un provider OAuth personalizzato rispetto a quello che hai collegato: http://davetimmins.wordpress.com/2013/04/12/using-oauth-with-arcgis-online -e-servicestack/- hai ragione. Non vedo alcun esempio che integri completamente i due, l'autenticazione OAuth insieme all'autorizzazione dei ruoli e delle autorizzazioni. –

risposta

6

Aggiornamento Su ulteriore riflessione, il tuo primo pensiero , creare un attributo RequiredScope sarebbe un modo più pulito per andare. Aggiungendo al gasdotto ServiceStack è facile come l'aggiunta dell'interfaccia IHasRequestFilter, l'implementazione di un filtro a richiesta personalizzato, come documentato qui: 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 
    } 

    ... 
} 

poi decorare la vostra DTO o servizi come hai delineato:

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

Il tuo filtro personalizzato RequireScope sarebbe quasi identico a ServiceStack's RequiredRoleAttribute implementation., quindi usalo come punto di partenza per il codice da.

In alternativa, è possibile mappare l'ambito su autorizzazione. Poi decorare il vostro DTO o servizio di conseguenza (see SS wiki per i dettagli), ad esempio:

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

Normalmente ServiceStack chiama il metodo bool hasPermission (il permesso di stringa) in IAuthSession. Questo metodo controlla se l'elenco elenca le autorizzazioni in IAuthSession contiene l'autorizzazione richiesta, quindi, in una IAuthSession personalizzata è possibile sovrascrivere HasPermission e inserire gli ambiti OAuth2 controllando lì.

+0

In realtà, sembra che tu voglia eseguire l'override di HasPermission, ma lo stesso principio si applica, il codice va nella tua implementazione di IAuthSession. –

+0

Rivisto la mia risposta per raccomandare un filtro di richiesta personalizzato come soluzione migliore rispetto alla creazione di una IAuthSession personalizzata e l'override di HasPermission() –

+0

Accettato per indicarmi la giusta direzione - grazie! - ma vedi la mia risposta per una piena implementazione e spiegazioni. –

8

OK, dopo un sacco di fare un passo attraverso le varie librerie con un debugger, penso che lo si fa in questo modo: https://github.com/dylanbeattie/OAuthStack

Ci sono due punti chiave dell'integrazione. In primo luogo, un attributo filtro personalizzato che viene utilizzato sul server per decorare gli endpoint di risorse che devono essere protetti con l'autorizzazione 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; 
     } 
    } 
} 

In secondo luogo, questo è il modo per collegare al client gasdotto ServiceStack HTTP e utilizzare DotNetOpenAuth per aggiungere la OAuth2 Authorization: Bearer {key} token per la richiesta in uscita:

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

richieste autorizzati avranno successo; le richieste con token mancante, token scaduto o scope insufficiente generano un WebServiceException

Questo è ancora molto roba proof-of-concept, ma sembra funzionare piuttosto bene. Sarei felice di ricevere feedback da chiunque conosca ServiceStack o DotNetOpenAuth meglio di me.

Problemi correlati