2017-01-04 77 views
7

Tworzę interfejs API i osobną aplikację typu front-end, która pobiera wymieniony interfejs API. W moim konkretnym przypadku używam Laravel Passport dla mojego API i niektórych VueJS dla mojej aplikacji frontendowej.Rejestracja użytkownika dla API/SPA

Aby użytkownik mógł utworzyć konto, użytkownik musi POST do trasy (/oauth/token) na API, które wymaga client_secret zostać przekazana (https://laravel.com/docs/5.3/passport#password-grant-tokens).

Jedynymi opcjami widzę to:

  1. posiadające client_secret wysłany jako nagłówek z mojego frontend aplikacji. Jednak wystawienie tego tokena na otwartej przestrzeni nie wydaje się mądre.
  2. Nie wymagają w ogóle client_secret. To nie wydaje się dużo lepsze niż opcja 1.
  3. Masz dynamiczną stronę na mojej aplikacji frontendowej, która może bezpiecznie przechowywać client_secret, a następnie wysłać ją do API. Chociaż jest to oczywiście najbezpieczniejsze, wydaje się, że częściowo pokonało to cel w pełni statycznej nakładki (SPA).

Jaka jest najlepsza praktyka dla tego rodzaju podejścia? Szukałem, jak to się robi w ogóle z API i SPA, ale nie znalazłem niczego, co wskazywałoby mi właściwy kierunek.

+0

Zaczynam myśleć, że dla rejestracji, CSRF lub innego zabezpieczenia opartego na tokenie nie ma znaczenia. Ponieważ rejestracja użytkownika jest prawdopodobnie publiczna, żadna ochrona po stronie klienta, która jest sprawdzana przez serwer, nie będzie bardzo skuteczna. – tptcat

Odpowiedz

4

Natknąłem się na ten sam problem i nie znalazłem więcej dokumentacji dotyczącej problemu.

Oto, co zrobiłem, wydaje mi się, że działa świetnie, powiesz mi, jeśli zauważysz coś nie tak.

W przypadku moich aplikacji będę używać klientów do przydzielania haseł, które będę tworzyć w locie dla każdego "klienta" mojej aplikacji. Przez klienta mam na myśli przeglądarkę, aplikację mobilną lub cokolwiek innego.

Każda przeglądarka sprawdza przy starcie, czy ma jakiekolwiek id_klienta i client_secret w localStorage (lub ciasteczka lub cokolwiek). Następnie, jeśli nie, wywołują punkt końcowy interfejsu API, który utworzy klienta udzielającego haseł i zwróci informacje do przeglądarki.

Przeglądarka będzie mogła zalogować użytkownika za pomocą tej nowej informacji o kliencie i jego danych uwierzytelniających.

Oto kontroler użyć do tworzenia klienta hasło Grant:

<?php 

namespace App\Http\Controllers\Api; 

use App\Http\Controllers\Controller; 
use Illuminate\Contracts\Hashing\Hasher; 
use Illuminate\Http\Request; 
use Laravel\Passport\ClientRepository; 

class AuthController extends Controller 
{ 
    protected $hasher; 

    protected $clients; 

    public function __construct (Hasher $hasher, ClientRepository $clients) 
    { 
     $this->hasher = $hasher; 
     $this->clients = $clients; 
    } 

    public function makeClient (Request $request) 
    { 
     $client = $this->clients->create(null,$request->header('User-Agent','Unknown Device'), '', false, true); 

     return $client->makeVisible('secret'); 
    } 
} 

Jak widać, jako nazwa dla klienta, staram się zachować User-Agent przeglądarki internetowej. Mogę potencjalnie wyświetlić stronę mojemu użytkownikowi z wszystkimi jego klientami i przyznać mu prawo do odwołania niektórych klientów, takich jak:

"Google Chrome, Nowy Jork". Możesz również przechowywać adres IP klienta lub cokolwiek, co pomoże ci dokładniej zidentyfikować typ urządzenia klienta ...

+0

Dzięki, @ El_Matella. Interesujące podejście. Domyślam się, że moje dotychczasowe pytanie brzmi: to podejście tak naprawdę nie uniemożliwia nikomu zobaczenia i złożenia wniosku o rejestrację URI rejestru/rejestracji, prawda? Czy czegoś brakuje? – tptcat

+0

Nie, niczego nie brakuje ... Nie wiem, jak temu zapobiec, z wyjątkiem próby ukrywania w kodzie niektórych tokenów, inżynier wsteczny nadal będzie mógł korzystać z tych tras. Jednak przy dobrej polityce bezpieczeństwa i analizie niektórych żądań, powinieneś być w stanie walczyć z tym ewentualnym ruchem ulicznym – Hammerbot

+0

Ok. Zastanawiam się, czy jest to znacznie gorsze od tradycyjnej aplikacji serwerowej, która używa CSRF na jednej z tych stron. Teoretycznie zły aktor może indeksować te strony, uzyskać CSRF i nadal przesyłać niechciane żądania do zaplecza. Jestem ciekaw innych opinii na ten temat. – tptcat

5

Z mojego punktu widzenia składnik Passport Laravel wydaje się nieprawidłowo implementować protokół ramowy OAuth2.

Parametry client_id i client_secret nie są częścią typu dotacji. W przypadku typu przydziału kwalifikatora właściciela zasobów, wymagane są następujące parametry: username i password (patrz RFC6749 section 4.3.2).

client_id i client_secret służą do uwierzytelniania poufnego klienta, który wysyła swoje poświadczenia za pomocą parametrów dotyczących ciała (patrz RFC6749 section 2.3.1). Składnik Laravel Passport powinien zezwalać na inne schematy uwierzytelniania klientów (w szczególności Podstawowy system uwierzytelniania HTTP). RFC6749 wskazuje również, że

Łącznie poświadczeń klienta w żądaniu ciała z użyciem dwóch parametrów nie jest zalecane i powinno być ograniczone do klientów niezdolnych bezpośrednio wykorzystać HTTP podstawowy schemat uwierzytelniania

Specyfikacja OpenID Connect Core wymienia niektóre z tych schematów w its section 9. Dokument RFC6749 nie wskazuje, w jaki sposób klienci publiczni (na przykład SPA) powinni uwierzytelniać się względem punktu końcowego tokena. Mają korzystać z typu grantu Implicit, który nie wymaga uwierzytelnienia klienta.

W każdym razie rozwiązaniem może być użycie pewnego rodzaju proxy. To proxy musi być zainstalowane na serwerze. Otrzyma wszystkie żądania od SPA (bez tajemnicy klienta), doda tajny klient i przekaże zmodyfikowane żądanie do punktu końcowego Laravel Passport. Następnie odpowiedź jest wysyłana do SPA. W ten sposób SPA nigdy nie ujawnia tajemnicy klienta.

+0

To rozwiązanie wydaje się najlepsze z mojego punktu widzenia, ale stwarza problem z paszportem Laravel: odwołanie tokenów.Klient nie może zalogować się z dwóch różnych urządzeń jednocześnie z tym samym klientem udzielania haseł, ponieważ zostanie rozłączony (przy tworzeniu nowego tokenu token udzielenia przydziału hasła odwołuje wszystkie inne tokeny tego klienta i użytkownika) – Hammerbot

+1

Nie ma powodu, aby serwer autoryzacji odwoływał poprzednie tokeny dostępu, ponieważ został wydany nowy. Jest to bardzo zły i niepożądany efekt uboczny, szczególnie dlatego, że [RFC7009] (https://tools.ietf.org/html/rfc7009) obejmuje to i pozwala klientowi zdecydować, kiedy token ma zostać odwołany. Jednakże, gdy patrzę na [kod źródłowy do paszportu] (https://github.com/laravel/passport/blob/0e8fe951996ed2a60b0b9a4012e21601a284f035/src/Passport.php#L24) domyślnym zachowaniem jest zachowanie istniejących tokenów dostępu. Wygląda na to, że dostępna jest opcja wyłączenia tego skutku ubocznego. Ho. –

+1

Ho! Masz rację, zmienili kod od mojej ostatniej wizyty. Zrobili to: https://github.com/laravel/passport/commit/b95791d42f42d405ffacdf21007f80ef7798b164, a następnie całkowicie refaktoryzowane kontrolerów, pozwalając phpleague wykonać całą pracę. Dobra robota! – Hammerbot

-1

Prostszym sposobem byłoby zajęcie się rejestracją użytkownika za pomocą aplikacji Laravel z uruchomionym Passport (a nie za pomocą interfejsu API aplikacji Vuejs).
Po zarejestrowaniu użytkownika i zalogowaniu się można użyć oprogramowania pośredniego Passport CreateFreshApiToken, aby dodać token do pliku cookie użytkownika podczas ładowania aplikacji frontendowej. Koniec z problemem client_secret.

Zobacz https://laravel.com/docs/5.3/passport#consuming-your-api-with-javascript i https://mattstauffer.co/blog/introducing-laravel-passport#super-powered-access-to-the-api-for-frontend-views

oauth/token także nie stwarza użytkownikowi wierzę? Ma dostarczać token (dla klienta akceptującego hasła) lub kod autoryzacyjny (klient udzielający zezwolenia na kod autoryzacyjny).

+0

W tym przypadku użycia aplikacja Frontend to oddzielna aplikacja od interfejsu API, więc ten przypadek użycia nie ma tutaj zastosowania. – tptcat