Uwierzytelnianie
View on GitHubUwierzytelnianie
Logowanie hasłem (AuthService, RegisteredUserController,
AuthenticatedSessionController) plus ścieżka logowania OAuth,
dzięki której użytkownik może zalogować się zewnętrznym dostawcą
tożsamości zamiast (albo obok) hasła w Orbit. GitHub jest pierwszym
podłączonym dostawcą (v0.9.5); Google i Microsoft są pokazane, ale
wyłączone w SocialLoginButtons, dopóki nie zostaną podłączone w ten
sam sposób.
Przewodniki, w kolejności, w jakiej naprawdę byś ich potrzebował
- Dodaj dostawcę OAuthUwierzytelnianieDodaj dostawcę OAuthPrzykład: podłączenie Google w ten sam sposób, w jaki GitHub jest podłączony dzisiaj, tak aby SocialLoginButtons mógł przestać go wyłączać. Ponieważ orbit-… — przykład podłączenia Google w ten sam sposób, w jaki GitHub jest podłączony dzisiaj.
Architektura w jednym akapicie
Orbit Local nigdy nie rozmawia bezpośrednio z endpointami OAuth
GitHuba i nigdy nie trzyma sekretu klienta GitHub OAuth App —
orbit-api jest scentralizowanym brokerem logowania, współdzielonym
przez każdą samodzielnie hostowaną instancję Orbit Local, korzystającym
z tego samego GitHub App/klienta OAuth, który już ma zainstalowany dla
integracji repozytorium (zobacz
../integrations/06-github-integration.mdIntegracjeIntegracja z GitHubemW przeciwieństwie do integracji typu notify (webhook) i import (pull) opisanych w innych przewodnikach z tej kategorii, GitHub to trzeci rodzaj: instalacja…).
Oznacza to, że samodzielnie hostowane wdrożenie dostaje "Sign in with
GitHub" za darmo, bez potrzeby zakładania przez operatora własnej
aplikacji GitHub OAuth. Przekazanie między dwoma bazami kodu
(src/auth/github-login w orbit-api, GithubAuthController w Local)
wygląda tak:
redirect()w Local generuje własny token CSRFstate, zapisuje go w sesji i przekierowuje przeglądarkę do orbit-apiGET /v1/auth/github/redirect?return_to=<własny URL callbacku Local, wraz ze state>.GitHubLoginService::start()w orbit-api zapisuje tenreturn_topod drugim, należącym do orbit-apistate(jego własna ochrona CSRF dla rundy z GitHubem —statez Local jest w tym momencie dla orbit-api tylko nieprzezroczystą częścią URL-a) i przekierowuje do prawdziwego URL-a autoryzacji GitHuba.- GitHub przekierowuje z powrotem na ustalony na stałe
GITHUB_LOGIN_CALLBACK_URLorbit-api.GitHubLoginService::callback()wymienia kod, pobiera profil (GET /user, z fallbackiem naGET /user/emailsdla prywatnego e-maila — zobaczgithub-user.client.ts), generuje losowy, jednorazowyexchange_tokenważny 60 sekund i przekierowuje przeglądarkę z powrotem do zapisanegoreturn_to(własny callback Local, z jegostatewciąż dołączonym), doklejającexchange_token. callback()w Local sprawdza swój własnystatewobec sesji (standardowa kontrola CSRF, niezwiązana z krokiem 2), a następnie wołaOrbitRelayClient::resolveGithubLoginToken($exchangeToken)— serwer-do-serwera, bez uwierzytelnienia (samo posiadanie tokena jest autoryzacją, dokładnie jak w przypadku kodu OAuth) — co wymienia token dokładnie raz naGithubIdentityDTO(githubId,githubUsername,email,name). Token dostępu GitHuba nigdy nie trafia do Local, a sam exchange_token staje się bezużyteczny zaraz po pierwszym (i jedynym) użyciu.
App\Services\GithubOAuthService::loginOrRegister()/linkToUser()/unlink()
nie zmieniają się przez to w ogóle — zawsze widzą tylko
GithubIdentityDTO, nigdy nie wiedząc ani nie dbając o to, czy
pochodzi on bezpośrednio z Socialite czy przez brokera.
loginOrRegister() rozstrzyga, w kolejności: istniejące dopasowanie po
github_id (logowanie), dopasowany zweryfikowany e-mail na
niepodlinkowanym koncie (auto-linkowanie — GitHub już udowodnił, że
wywołujący jest właścicielem tego e-maila), albo, gdy żadne z powyższych
nie zachodzi, zupełnie nowe konto bez hasła (kolumna users.password
jest nullable właśnie z tego powodu).
To osobna tożsamość od integracji GitHub App
(app/Services/Integrations/Github). Tamta jest przypisana do
projektu i daje dostęp do repozytorium przez installation token; ta
jest przypisana do użytkownika i tylko udowadnia, że "to konto Orbit
należy do tego konta GitHub". Jedno nie implikuje drugiego — projekt
może być połączony z GitHub bez podlinkowanego konta osobistego
nikogo, a użytkownik może podlinkować swoje konto GitHub, mimo że jego
projekt w ogóle nie ma integracji z GitHub.
Użycie linku jako bramki gdzie indziej
Podlinkowane konto GitHub jest też używane jako warunek wstępny dla
niepowiązanej akcji: utworzenie brancha lub pull requesta z poziomu
issue (IssueGithubDevelopmentController::assertGithubAccountLinked())
wymaga $user->github_id !== null, sprawdzanego osobno od
uprawnienia createGithubDevelopment (IssuePolicy) — uprawnienie
pyta "czy twoja rola na to pozwala", to pytanie brzmi "czy udowodniłeś,
kim jesteś na GitHubie". Przy podobnej bramce w przyszłej funkcji
podążaj za tym wzorcem (mała prywatna metoda-strażnik rzucająca
ValidationException z kluczem pola, którego frontend już oczekuje,
np. branch/pullRequest), zamiast wplatać sprawdzenie tożsamości w
politykę.
Bezpieczeństwo odlinkowywania
GithubOAuthService::unlink() odmawia wyczyszczenia github_id, gdy
users.password jest puste — konto istniejące tylko dzięki GitHubowi
zostałoby inaczej trwale zablokowane. Strona ustawień Security & Access
(AccountSettingsSecurityTab) blokuje z tego samego powodu przycisk
"Unlink", ale to sprawdzenie po stronie backendu faktycznie ma
znaczenie; blokada we frontendzie to tylko UX.
