Jak zabezpieczyć Identity zaplecze internetowego interfejsu API dla spA
Uwaga
Nie jest to najnowsza wersja tego artykułu. Aby zapoznać się z bieżącą wersją, zobacz wersję tego artykułu platformy .NET 9.
Ważne
Te informacje odnoszą się do produktu w wersji wstępnej, który może zostać znacząco zmodyfikowany, zanim zostanie wydany komercyjnie. Firma Microsoft nie udziela żadnych gwarancji, jawnych lub domniemanych, w odniesieniu do informacji podanych w tym miejscu.
Aby zapoznać się z bieżącą wersją, zobacz wersję tego artykułu platformy .NET 9.
ASP.NET Core Identity udostępnia interfejsy API obsługujące uwierzytelnianie, autoryzację i identity zarządzanie. Interfejsy API umożliwiają zabezpieczanie punktów końcowych zaplecza internetowego interfejsu API przy użyciu cookieuwierzytelniania opartego na protokole . Opcja oparta na tokenach jest dostępna dla klientów, którzy nie mogą używać plików cookie, ale w tym celu ponosisz odpowiedzialność za zapewnienie bezpieczeństwa tokenów. Zalecamy używanie plików cookie dla aplikacji opartych na przeglądarce, ponieważ domyślnie przeglądarka automatycznie obsługuje je bez uwidaczniania ich w języku JavaScript.
W tym artykule przedstawiono sposób zabezpieczania Identity zaplecza internetowego interfejsu API dla spA, takich jak aplikacje Angular, React i Vue. Te same interfejsy API zaplecza mogą służyć do zabezpieczania Blazor WebAssembly aplikacji.
Wymagania wstępne
Kroki przedstawione w tym artykule dodają uwierzytelnianie i autoryzację do aplikacji internetowego interfejsu API platformy ASP.NET Core, która:
- Nie jest jeszcze skonfigurowany do uwierzytelniania.
- Obiekty docelowe
net8.0
lub nowsze. - Może to być minimalny interfejs API lub interfejs API oparty na kontrolerze.
Niektóre instrukcje testowania w tym artykule używają interfejsu użytkownika struktury Swagger dołączonego do szablonu projektu. Interfejs użytkownika struktury Swagger nie jest wymagany do użycia Identity z zapleczem internetowego interfejsu API.
Instalowanie pakietów NuGet
Zainstaluj następujące pakiety NuGet:
Microsoft.AspNetCore.Identity.EntityFrameworkCore
— Umożliwia Identity pracę z programem Entity Framework Core (EF Core).- Taki, który umożliwia EF Core pracę z bazą danych, taką jak jeden z następujących pakietów:
Aby uzyskać najszybszy sposób rozpoczęcia pracy, użyj bazy danych w pamięci.
Zmień bazę danych później na SQLite lub SQL Server, aby zapisać dane użytkownika między sesjami podczas testowania lub użycia produkcyjnego. Wprowadza to pewną złożoność w porównaniu z pamięcią, ponieważ wymaga utworzenia bazy danych za pośrednictwem migracji, jak pokazano w samouczku EF Corewprowadzającym.
Zainstaluj te pakiety przy użyciu menedżera pakietów NuGet w programie Visual Studio lub polecenia dotnet add package CLI.
Tworzenie elementu IdentityDbContext
Dodaj klasę o nazwie ApplicationDbContext
, która dziedziczy z klasy IdentityDbContext<TUser>:
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
public class ApplicationDbContext : IdentityDbContext<IdentityUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) :
base(options)
{ }
}
Pokazany kod udostępnia specjalny konstruktor, który umożliwia skonfigurowanie bazy danych dla różnych środowisk.
Dodaj co najmniej jedną z poniższych using
dyrektyw zgodnie z potrzebami podczas dodawania kodu pokazanego w tych krokach.
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
EF Core Konfigurowanie kontekstu
Jak wspomniano wcześniej, najprostszym sposobem rozpoczęcia pracy jest użycie bazy danych w pamięci. W przypadku uruchamiania w pamięci każde uruchomienie rozpoczyna się od nowej bazy danych i nie ma potrzeby używania migracji. Po wywołaniu metody WebApplication.CreateBuilder(args)
dodaj następujący kod, aby skonfigurować Identity używanie bazy danych w pamięci:
builder.Services.AddDbContext<ApplicationDbContext>(
options => options.UseInMemoryDatabase("AppDb"));
Aby zapisać dane użytkownika między sesjami podczas testowania lub użycia produkcyjnego, zmień bazę danych później na SQLite lub SQL Server.
Dodawanie Identity usług do kontenera
Po wywołaniu metody wywołaj metodę WebApplication.CreateBuilder(args)
, wywołaj metodę AddAuthorization , aby dodać usługi do kontenera wstrzykiwania zależności (DI):
builder.Services.AddAuthorization();
Aktywowanie Identity interfejsów API
Po wywołaniu metody wywołania wywołaj metodę WebApplication.CreateBuilder(args)
AddIdentityApiEndpoints<TUser>(IServiceCollection) i AddEntityFrameworkStores<TContext>(IdentityBuilder).
builder.Services.AddIdentityApiEndpoints<IdentityUser>()
.AddEntityFrameworkStores<ApplicationDbContext>();
Domyślnie zarówno pliki cookie, jak i zastrzeżone tokeny są aktywowane. Pliki cookie i tokeny są wystawiane podczas logowania, jeśli useCookies
parametr ciągu zapytania w punkcie końcowym logowania to true
.
Mapuj Identity trasy
Po wywołaniu metody builder.Build()
wywołania metody wywołaj metodę MapIdentityApi<TUser>(IEndpointRouteBuilder) , aby zamapować Identity punkty końcowe:
app.MapIdentityApi<IdentityUser>();
Zabezpieczanie wybranych punktów końcowych
Aby zabezpieczyć punkt końcowy, użyj RequireAuthorization metody rozszerzenia w wywołaniu Map{Method}
definiującym trasę. Na przykład:
app.MapGet("/weatherforecast", (HttpContext httpContext) =>
{
var forecast = Enumerable.Range(1, 5).Select(index =>
new WeatherForecast
{
Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = summaries[Random.Shared.Next(summaries.Length)]
})
.ToArray();
return forecast;
})
.WithName("GetWeatherForecast")
.WithOpenApi()
.RequireAuthorization();
Metody RequireAuthorization
można również użyć do:
Zabezpieczanie punktów końcowych interfejsu użytkownika struktury Swagger, jak pokazano w poniższym przykładzie:
app.MapSwagger().RequireAuthorization();
Zabezpiecz się przy użyciu określonego oświadczenia lub uprawnienia, jak pokazano w poniższym przykładzie:
.RequireAuthorization("Admin");
W projekcie internetowego interfejsu API opartego na kontrolerze zabezpieczanie punktów końcowych przez zastosowanie atrybutu [Authorize
] do kontrolera lub akcji.
Testowanie interfejsu API
Szybkim sposobem testowania uwierzytelniania jest użycie bazy danych w pamięci i interfejsu użytkownika programu Swagger dołączonego do szablonu projektu. W poniższych krokach pokazano, jak przetestować interfejs API za pomocą interfejsu użytkownika struktury Swagger. Upewnij się, że punkty końcowe interfejsu użytkownika struktury Swagger nie są zabezpieczone.
Próba uzyskania dostępu do zabezpieczonego punktu końcowego
- Uruchom aplikację i przejdź do interfejsu użytkownika programu Swagger.
- Rozwiń zabezpieczony punkt końcowy, taki jak
/weatherforecast
w projekcie utworzonym przez szablon internetowego interfejsu API. - Wybierz pozycję Wypróbuj.
- Wybierz polecenie Wykonaj. Odpowiedź to
401 - not authorized
.
Testowanie rejestracji
Rozwiń
/register
i wybierz pozycję Wypróbuj.W sekcji Parametry interfejsu użytkownika jest wyświetlana przykładowa treść żądania:
{ "email": "string", "password": "string" }
Zastąp ciąg prawidłowym adresem e-mail i hasłem, a następnie wybierz pozycję Wykonaj.
Aby zachować zgodność z domyślnymi regułami sprawdzania poprawności haseł, hasło musi mieć długość co najmniej sześciu znaków i zawierać co najmniej jeden z następujących znaków:
- Wielka litera
- Mała litera
- Cyfra liczbowa
- Znak niefanumeryczny
Jeśli wprowadzisz nieprawidłowy adres e-mail lub nieprawidłowe hasło, wynik zawiera błędy walidacji. Oto przykład treści odpowiedzi z błędami walidacji:
{ "type": "https://tools.ietf.org/html/rfc9110#section-15.5.1", "title": "One or more validation errors occurred.", "status": 400, "errors": { "PasswordTooShort": [ "Passwords must be at least 6 characters." ], "PasswordRequiresNonAlphanumeric": [ "Passwords must have at least one non alphanumeric character." ], "PasswordRequiresDigit": [ "Passwords must have at least one digit ('0'-'9')." ], "PasswordRequiresLower": [ "Passwords must have at least one lowercase ('a'-'z')." ] } }
Błędy są zwracane w formacie ProblemDetails , aby klient mógł je analizować i wyświetlać błędy walidacji zgodnie z potrzebami.
Pomyślna rejestracja powoduje
200 - OK
odpowiedź.
Testowanie logowania
Rozwiń
/login
i wybierz pozycję Wypróbuj. Przykładowa treść żądania zawiera dwa dodatkowe parametry:{ "email": "string", "password": "string", "twoFactorCode": "string", "twoFactorRecoveryCode": "string" }
Dodatkowe właściwości JSON nie są potrzebne w tym przykładzie i można je usunąć. Ustaw wartość opcji
useCookies
natrue
.Zastąp ciąg ciąg adresem e-mail i hasłem użytym do zarejestrowania, a następnie wybierz pozycję Wykonaj.
Pomyślne zalogowanie powoduje wyświetlenie
200 - OK
odpowiedzi z nagłówkiem cookie odpowiedzi.
Ponowne testowanie zabezpieczonego punktu końcowego
Po pomyślnym zalogowaniu uruchom ponownie zabezpieczony punkt końcowy. Uwierzytelnianie cookie jest wysyłane automatycznie z żądaniem, a punkt końcowy jest autoryzowany. CookieUwierzytelnianie oparte na protokole jest bezpiecznie wbudowane w przeglądarkę i "działa".
Testowanie przy użyciu klientów innych niżbrowser
Niektórzy klienci sieci Web mogą domyślnie nie dołączać plików cookie do nagłówka:
Jeśli używasz narzędzia do testowania interfejsów API, może być konieczne włączenie plików cookie w ustawieniach.
Interfejs API języka JavaScript
fetch
domyślnie nie zawiera plików cookie. Włącz je, ustawiająccredentials
wartośćinclude
w opcjach.Uruchomienie
HttpClient
w Blazor WebAssembly aplikacji wymagaHttpRequestMessage
uwzględnienia poświadczeń, takich jak w poniższym przykładzie:request.SetBrowserRequestCredential(BrowserRequestCredentials.Include);
Korzystanie z uwierzytelniania opartego na tokenach
Zalecamy używanie plików cookie w aplikacjach opartych na przeglądarce, ponieważ domyślnie przeglądarka automatycznie obsługuje je bez ujawniania ich w języku JavaScript.
Wystawiony jest token niestandardowy (zastrzeżony dla platformy ASP.NET Core identity ), który może służyć do uwierzytelniania kolejnych żądań. Token jest przekazywany w nagłówku Authorization
jako token elementu nośnego. Dostępny jest również token odświeżania. Ten token umożliwia aplikacji żądanie nowego tokenu po wygaśnięciu starego tokenu bez wymuszania ponownego zalogowania się użytkownika.
Tokeny nie są standardowymi tokenami internetowymi JSON (JWTs). Użycie tokenów niestandardowych jest zamierzone, ponieważ wbudowany Identity interfejs API jest przeznaczony głównie dla prostych scenariuszy. Opcja tokenu nie ma być w pełni funkcjonalnym identity dostawcą usług lub serwerem tokenów, ale zamiast tego alternatywą dla cookie klientów, którzy nie mogą używać plików cookie.
Aby użyć uwierzytelniania opartego useCookies
na tokenach, ustaw parametr ciągu zapytania na false
wartość podczas wywoływania punktu końcowego /login
. Tokeny używają schematu uwierzytelniania elementu nośnego. Użycie tokenu zwróconego z wywołania metody do /login
metody , kolejne wywołania chronionych punktów końcowych powinny dodać nagłówek Authorization: Bearer <token>
, w którym <token>
znajduje się token dostępu. Aby uzyskać więcej informacji, zobacz Używanie punktu końcowego POST /login
w dalszej części tego artykułu.
Wyloguj się
Aby umożliwić użytkownikowi wylogowanie się, zdefiniuj /logout
punkt końcowy podobny do następującego przykładu:
app.MapPost("/logout", async (SignInManager<IdentityUser> signInManager,
[FromBody] object empty) =>
{
if (empty != null)
{
await signInManager.SignOutAsync();
return Results.Ok();
}
return Results.Unauthorized();
})
.WithOpenApi()
.RequireAuthorization();
Podaj pusty obiekt JSON ({}
) w treści żądania podczas wywoływania tego punktu końcowego. Poniższy kod to przykład wywołania punktu końcowego wylogowania:
public signOut() {
return this.http.post('/logout', {}, {
withCredentials: true,
observe: 'response',
responseType: 'text'
Punkty MapIdentityApi<TUser>
końcowe
Wywołanie metody w celu MapIdentityApi<TUser>
dodawania następujących punktów końcowych do aplikacji:
POST /register
POST /login
POST /refresh
GET /confirmEmail
POST /resendConfirmationEmail
POST /forgotPassword
POST /resetPassword
POST /manage/2fa
GET /manage/info
POST /manage/info
Korzystanie z punktu końcowego POST /register
Treść żądania musi mieć Email właściwości i Password :
{
"email": "string",
"password": "string",
}
Aby uzyskać więcej informacji, zobacz:
- Przetestuj rejestrację wcześniej w tym artykule.
- RegisterRequest.
Korzystanie z punktu końcowego POST /login
W treści Email żądania i Password są wymagane. Jeśli włączono uwierzytelnianie dwuskładnikowe (2FA), TwoFactorCode TwoFactorRecoveryCode albo jest wymagane. Jeśli uwierzytelnianie 2FA nie jest włączone, pomiń zarówno uwierzytelnianie, jak twoFactorCode
i twoFactorRecoveryCode
. Aby uzyskać więcej informacji, zobacz Używanie punktu końcowego POST /manage/2fa
w dalszej części tego artykułu.
Oto przykład treści żądania z nie włączoną uwierzytelnianiem 2FA:
{
"email": "string",
"password": "string"
}
Oto przykłady treści żądań z włączoną usługą 2FA:
-
{ "email": "string", "password": "string", "twoFactorCode": "string", }
-
{ "email": "string", "password": "string", "twoFactorRecoveryCode": "string" }
Punkt końcowy oczekuje parametru ciągu zapytania:
useCookies
- Ustaw natrue
wartość dla uwierzytelniania opartego na cookie. Ustaw wartość lubfalse
pomiń uwierzytelnianie oparte na tokenach.
Aby uzyskać więcej informacji na temat uwierzytelniania opartego na cookieprotokole, zobacz Testowanie logowania we wcześniejszej sekcji tego artykułu.
Uwierzytelnianie oparte na tokenach
Jeśli useCookies
zostanie false
pominięte lub pominięte, jest włączone uwierzytelnianie oparte na tokenach. Treść odpowiedzi zawiera następujące właściwości:
{
"tokenType": "string",
"accessToken": "string",
"expiresIn": 0,
"refreshToken": "string"
}
Aby uzyskać więcej informacji na temat tych właściwości, zobacz AccessTokenResponse.
Umieść token dostępu w nagłówku, aby wysyłać uwierzytelnione żądania, jak pokazano w poniższym przykładzie
Authorization: Bearer {access token}
Gdy token dostępu wkrótce wygaśnie, wywołaj punkt końcowy /refresh .
Korzystanie z punktu końcowego POST /refresh
Do użycia tylko w przypadku uwierzytelniania opartego na tokenach. Pobiera nowy token dostępu bez wymuszania ponownego logowania użytkownika. Wywołaj ten punkt końcowy, gdy token dostępu wkrótce wygaśnie.
Treść żądania zawiera tylko element RefreshToken. Oto przykład treści żądania:
{
"refreshToken": "string"
}
Jeśli wywołanie zakończy się pomyślnie, treść odpowiedzi to nowy AccessTokenResponseelement , jak pokazano w poniższym przykładzie:
{
"tokenType": "string",
"accessToken": "string",
"expiresIn": 0,
"refreshToken": "string"
}
Korzystanie z punktu końcowego GET /confirmEmail
Jeśli Identity skonfigurowana jest opcja potwierdzenia wiadomości e-mail, pomyślne wywołanie /register
punktu końcowego spowoduje wysłanie wiadomości e-mail zawierającej link do punktu końcowego /confirmEmail
. Link zawiera następujące parametry ciągu zapytania:
userId
code
changedEmail
- Uwzględnione tylko wtedy, gdy użytkownik zmienił adres e-mail podczas rejestracji.
Identity Zawiera domyślny tekst wiadomości e-mail z potwierdzeniem. Domyślnie temat wiadomości e-mail to "Potwierdź wiadomość e-mail", a treść wiadomości e-mail wygląda następująco:
Please confirm your account by <a href='https://contoso.com/confirmEmail?userId={user ID}&code={generated code}&changedEmail={new email address}'>clicking here</a>.
RequireConfirmedEmail Jeśli właściwość jest ustawiona na true
, użytkownik nie może się zalogować, dopóki adres e-mail nie zostanie potwierdzony, klikając link w wiadomości e-mail. Punkt /confirmEmail
końcowy:
- Potwierdza adres e-mail i umożliwia użytkownikowi logowanie się.
- Zwraca tekst "Dziękujemy za potwierdzenie wiadomości e-mail" w treści odpowiedzi.
Aby skonfigurować Identity potwierdzenie wiadomości e-mail, dodaj kod , Program.cs
aby ustawić RequireConfirmedEmail
wartość i true
dodać klasę implementającą IEmailSender do kontenera DI. Na przykład:
builder.Services.Configure<IdentityOptions>(options =>
{
options.SignIn.RequireConfirmedEmail = true;
});
builder.Services.AddTransient<IEmailSender, EmailSender>();
Aby uzyskać więcej informacji, zobacz Potwierdzanie konta i odzyskiwanie hasła w usłudze ASP.NET Core.
Identity Udostępnia domyślny tekst dla innych wiadomości e-mail, które należy również wysłać, na przykład w przypadku uwierzytelniania 2FA i resetowania hasła. Aby dostosować te wiadomości e-mail, podaj niestandardową implementację interfejsu IEmailSender
. W poprzednim przykładzie jest to klasa, EmailSender
która implementuje IEmailSender
element . Aby uzyskać więcej informacji, w tym przykład klasy, która implementuje IEmailSender
program , zobacz Potwierdzanie konta i odzyskiwanie hasła w programie ASP.NET Core.
Korzystanie z punktu końcowego POST /resendConfirmationEmail
Wysyła wiadomość e-mail tylko wtedy, gdy adres jest prawidłowy dla zarejestrowanego użytkownika.
Treść żądania zawiera tylko element Email. Oto przykład treści żądania:
{
"email": "string"
}
Aby uzyskać więcej informacji, zobacz Używanie punktu końcowego GET /confirmEmail
wcześniej w tym artykule.
Korzystanie z punktu końcowego POST /forgotPassword
Generuje wiadomość e-mail zawierającą kod resetowania hasła. Wyślij ten kod na /resetPassword
adres przy użyciu nowego hasła.
Treść żądania zawiera tylko element Email. Oto przykład:
{
"email": "string"
}
Aby uzyskać informacje na temat włączania Identity wysyłania wiadomości e-mail, zobacz Korzystanie z punktu końcowegoGET /confirmEmail
.
Korzystanie z punktu końcowego POST /resetPassword
Wywołaj ten punkt końcowy po otrzymaniu kodu resetowania /forgotPassword
, wywołując punkt końcowy.
Treść żądania wymaga , EmailResetCodei NewPassword. Oto przykład:
{
"email": "string",
"resetCode": "string",
"newPassword": "string"
}
Korzystanie z punktu końcowego POST /manage/2fa
Konfiguruje uwierzytelnianie dwuskładnikowe (2FA) dla użytkownika. Po włączeniu uwierzytelniania 2FA pomyślne logowanie wymaga kodu wygenerowanego przez aplikację wystawcy uwierzytelniania oprócz adresu e-mail i hasła.
Włączanie uwierzytelniania 2FA
Aby włączyć uwierzytelnianie 2FA dla aktualnie uwierzytelnioowanego użytkownika:
Wywołaj
/manage/2fa
punkt końcowy, wysyłając pusty obiekt JSON ({}
) w treści żądania.Treść odpowiedzi udostępnia SharedKey wraz z innymi właściwościami, które nie są w tym momencie potrzebne. Klucz wspólny służy do konfigurowania aplikacji authenticator. Przykład treści odpowiedzi:
{ "sharedKey": "string", "recoveryCodesLeft": 0, "recoveryCodes": null, "isTwoFactorEnabled": false, "isMachineRemembered": false }
Użyj klucza współużytkowanego, aby uzyskać jednorazowe hasło oparte na czasie (TOTP). Aby uzyskać więcej informacji, zobacz Enable QR code generation for TOTP authenticator apps in ASP.NET Core (Włączanie generowania kodu QR dla aplikacji uwierzytelniania TOTP w usłudze ASP.NET Core).
Wywołaj
/manage/2fa
punkt końcowy, wysyłając element TOTP i"enable": true
w treści żądania. Na przykład:{ "enable": true, "twoFactorCode": "string" }
Treść odpowiedzi potwierdza, że IsTwoFactorEnabled ma wartość true i udostępnia element RecoveryCodes. Kody odzyskiwania są używane do logowania się, gdy aplikacja wystawcy uwierzytelniania nie jest dostępna. Przykład treści odpowiedzi po pomyślnym włączeniu uwierzytelniania 2FA:
{ "sharedKey": "string", "recoveryCodesLeft": 10, "recoveryCodes": [ "string", "string", "string", "string", "string", "string", "string", "string", "string", "string" ], "isTwoFactorEnabled": true, "isMachineRemembered": false }
Logowanie przy użyciu uwierzytelniania 2FA
Wywołaj /login
punkt końcowy, wysyłając adres e-mail, hasło i toTP w treści żądania. Na przykład:
{
"email": "string",
"password": "string",
"twoFactorCode": "string"
}
Jeśli użytkownik nie ma dostępu do aplikacji wystawcy uwierzytelniania, zaloguj się, wywołując /login
punkt końcowy przy użyciu jednego z kodów odzyskiwania podanych po włączeniu uwierzytelniania 2FA. Treść żądania wygląda podobnie do następującego przykładu:
{
"email": "string",
"password": "string",
"twoFactorRecoveryCode": "string"
}
Resetowanie kodów odzyskiwania
Aby uzyskać nową kolekcję kodów odzyskiwania, wywołaj ten punkt końcowy z ustawieniem ResetRecoveryCodes na true
. Oto przykład treści żądania:
{
"resetRecoveryCodes": true
}
Resetowanie klucza współużytkowanego
Aby uzyskać nowy losowy klucz współużytkowany, wywołaj ten punkt końcowy z ustawionym ResetSharedKey na true
. Oto przykład treści żądania:
{
"resetSharedKey": true
}
Zresetowanie klucza powoduje automatyczne wyłączenie wymagania logowania dwuskładnikowego dla uwierzytelnionego użytkownika do momentu ponownego włączenia go przez późniejsze żądanie.
Zapomnij o maszynie
Aby wyczyścić flagę cookie "zapamiętaj mnie", jeśli jest obecna, wywołaj ten punkt końcowy z ustawionym wartością ForgetMachine true. Oto przykład treści żądania:
{
"forgetMachine": true
}
Ten punkt końcowy nie ma wpływu na uwierzytelnianie oparte na tokenach.
Korzystanie z punktu końcowego GET /manage/info
Pobiera adres e-mail i stan potwierdzenia wiadomości e-mail zalogowanego użytkownika. Oświadczenia zostały pominięte z tego punktu końcowego ze względów bezpieczeństwa. Jeśli są potrzebne oświadczenia, użyj interfejsów API po stronie serwera, aby skonfigurować punkt końcowy dla oświadczeń. Lub zamiast udostępniać wszystkie oświadczenia użytkowników, podaj punkt końcowy weryfikacji, który akceptuje oświadczenie i odpowiada, czy użytkownik ma je.
Żądanie nie wymaga żadnych parametrów. Treść odpowiedzi zawiera Email właściwości i IsEmailConfirmed , jak w poniższym przykładzie:
{
"email": "string",
"isEmailConfirmed": true
}
Korzystanie z punktu końcowego POST /manage/info
Aktualizuje adres e-mail i hasło zalogowanego użytkownika. Wyślij NewEmailelement , NewPasswordi OldPassword w treści żądania, jak pokazano w poniższym przykładzie:
{
"newEmail": "string",
"newPassword": "string",
"oldPassword": "string"
}
Oto przykład treści odpowiedzi:
{
"email": "string",
"isEmailConfirmed": false
}
Zobacz też
Aby uzyskać więcej informacji, zobacz następujące zasoby:
- identity Wybieranie rozwiązania do zarządzania
- Identity rozwiązania do zarządzania dla aplikacji internetowych platformy .NET
- Prosta autoryzacja w ASP.NET Core
- Dodawanie, pobieranie i usuwanie danych użytkownika w Identity projekcie ASP.NET Core
- Tworzenie aplikacji platformy ASP.NET Core przy użyciu danych użytkowników chronionych przez autoryzację
- Potwierdzenie konta i odzyskiwanie hasła w usłudze ASP.NET Core
- Włączanie generowania kodu QR dla aplikacji uwierzytelniającego TOTP w usłudze ASP.NET Core
- Przykładowe zaplecze internetowego interfejsu API dla spAs Plik HTTP pokazuje uwierzytelnianie oparte na tokenach. Na przykład: .
- Nie ustawia
useCookies
- Używa nagłówka autoryzacji do przekazania tokenu
- Pokazuje odświeżanie w celu rozszerzenia sesji bez wymuszania ponownego logowania użytkownika
- Nie ustawia
- Przykładowa aplikacja Angular używana Identity do zabezpieczania zaplecza internetowego interfejsu API
Szablony ASP.NET Core oferują uwierzytelnianie w aplikacjach jednostronicowych (SPA) przy użyciu obsługi autoryzacji interfejsu API. ASP.NET Core Identity na potrzeby uwierzytelniania i przechowywania użytkowników jest połączony z serwerem Duende Identity do implementowania programu OpenID Connect.
Ważne
Oprogramowanie Duende może wymagać zapłacenia opłaty licencyjnej za korzystanie z serwera Duende Identity Server w środowisku produkcyjnym. Aby uzyskać więcej informacji, zobacz Migracja z platformy ASP.NET Core w wersji 5.0 do wersji 6.0.
Parametr uwierzytelniania został dodany do szablonów projektów Angular i React , które są podobne do parametru uwierzytelniania w szablonach projektów Aplikacji internetowej (Model-View-Controller) i Aplikacji internetowej (Razor Pages). Dozwolone wartości parametrów to None i Individual. Szablon projektu React.js i Redux nie obsługuje obecnie parametru uwierzytelniania.
Tworzenie aplikacji z obsługą autoryzacji interfejsu API
Uwierzytelnianie i autoryzacja użytkownika mogą być używane zarówno z usługami Angular, jak i React SPA. Otwórz powłokę poleceń i uruchom następujące polecenie:
Angular:
dotnet new angular -au Individual
React:
dotnet new react -au Individual
Poprzednie polecenie tworzy aplikację ASP.NET Core z katalogiem ClientApp zawierającym SPA.
Ogólny opis składników ASP.NET Core aplikacji
W poniższych sekcjach opisano dodatki do projektu, gdy jest uwzględniona obsługa uwierzytelniania:
Program.cs
Poniższe przykłady kodu bazują na pakiecie NuGet Microsoft.AspNetCore.ApiAuthorization.IdentityServer . Przykłady umożliwiają skonfigurowanie uwierzytelniania i autoryzacji interfejsu AddApiAuthorization API przy użyciu metod i AddIdentityServerJwt rozszerzeń. Projekty korzystające z szablonów projektów React lub Angular SPA z uwierzytelnianiem obejmują odwołanie do tego pakietu.
dotnet new angular -au Individual
generuje następujący Program.cs
plik:
using Microsoft.AspNetCore.Authentication;
using Microsoft.EntityFrameworkCore;
using output_directory_name.Data;
using output_directory_name.Models;
var builder = WebApplication.CreateBuilder(args);
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection") ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlite(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
builder.Services.AddAuthentication()
.AddIdentityServerJwt();
builder.Services.AddControllersWithViews();
builder.Services.AddRazorPages();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseMigrationsEndPoint();
}
else
{
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseIdentityServer();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
app.MapRazorPages();
app.MapFallbackToFile("index.html");
app.Run();
Powyższy kod konfiguruje:
Identity z domyślnym interfejsem użytkownika:
builder.Services.AddDbContext<ApplicationDbContext>(options => options.UseSqlite(connectionString)); builder.Services.AddDatabaseDeveloperPageExceptionFilter(); builder.Services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true) .AddEntityFrameworkStores<ApplicationDbContext>();
IdentityServer z dodatkową
AddApiAuthorization
metodą pomocnika, która konfiguruje niektóre domyślne konwencje ASP.NET Core na serwerze IdentityServer:builder.Services.AddIdentityServer() .AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
Uwierzytelnianie przy użyciu dodatkowej
AddIdentityServerJwt
metody pomocniczej, która konfiguruje aplikację do weryfikowania tokenów JWT utworzonych przez usługę IdentityServer:builder.Services.AddAuthentication() .AddIdentityServerJwt();
Oprogramowanie pośredniczące uwierzytelniania, które jest odpowiedzialne za weryfikowanie poświadczeń żądania i ustawianie użytkownika w kontekście żądania:
app.UseAuthentication();
Oprogramowanie pośredniczące IdentityServer, które uwidacznia punkty końcowe OpenID Connect:
app.UseIdentityServer();
Ostrzeżenie
W tym artykule przedstawiono użycie parametry połączenia. W przypadku lokalnej bazy danych użytkownik nie musi być uwierzytelniany, ale w środowisku produkcyjnym parametry połączenia czasami zawiera hasło do uwierzytelniania. Poświadczenie hasła właściciela zasobu (ROPC) jest zagrożeniem bezpieczeństwa, którego należy unikać w produkcyjnych bazach danych. Aplikacje produkcyjne powinny korzystać z najbezpieczniejszego dostępnego przepływu uwierzytelniania. Aby uzyskać więcej informacji na temat uwierzytelniania aplikacji wdrożonych w środowiskach testowych lub produkcyjnych, zobacz Bezpieczne przepływy uwierzytelniania.
usługa aplikacja systemu Azure w systemie Linux
W przypadku wdrożeń usługi aplikacja systemu Azure w systemie Linux określ wystawcę jawnie:
builder.Services.Configure<JwtBearerOptions>(
IdentityServerJwtConstants.IdentityServerJwtBearerScheme,
options =>
{
options.Authority = "{AUTHORITY}";
});
W poprzednim kodzie {AUTHORITY}
symbol zastępczy jest Authority używany podczas wykonywania wywołań OpenID Connect.
Przykład:
options.Authority = "https://contoso-service.azurewebsites.net";
AddApiAuthorization
Ta metoda pomocnika konfiguruje maszynę wirtualną IdentityServer do korzystania z naszej obsługiwanej konfiguracji. IdentityServer to zaawansowana i rozszerzalna struktura do obsługi problemów z zabezpieczeniami aplikacji. Jednocześnie uwidacznia niepotrzebną złożoność dla najbardziej typowych scenariuszy. W związku z tym dostępny jest zestaw konwencji i opcji konfiguracji, które są uważane za dobry punkt wyjścia. Po zmianie potrzeb uwierzytelniania pełna moc serwera IdentityServer jest nadal dostępna w celu dostosowania uwierzytelniania zgodnie z potrzebami.
AddIdentityServerJwt
Ta metoda pomocnika konfiguruje schemat zasad dla aplikacji jako domyślną procedurę obsługi uwierzytelniania. Zasady są skonfigurowane tak, aby umożliwić Identity obsługę wszystkich żądań kierowanych do dowolnej ścieżki podrzędnej Identity w obszarze adresu URL "/Identity". Usługa JwtBearerHandler
obsługuje wszystkie inne żądania. Ponadto ta metoda rejestruje zasób interfejsu <<ApplicationName>>API
API za pomocą klasy IdentityServer z domyślnym zakresem <<ApplicationName>>API
i konfiguruje oprogramowanie pośredniczące tokenu elementu nośnego JWT w celu sprawdzania poprawności tokenów wystawionych przez usługę IdentityServer dla aplikacji.
WeatherForecastController
W pliku zwróć uwagę [Authorize]
na atrybut zastosowany do klasy, który wskazuje, że użytkownik musi być autoryzowany na podstawie domyślnych zasad dostępu do zasobu. Domyślne zasady autoryzacji mają być skonfigurowane tak, aby używały domyślnego schematu uwierzytelniania, który został skonfigurowany przez AddIdentityServerJwt
schemat zasad wymienionych powyżej, dzięki czemu JwtBearerHandler
skonfigurowana przez taką metodę pomocnika domyślna procedura obsługi żądań do aplikacji.
ApplicationDbContext
W pliku zwróć uwagę, że to samo DbContext
jest używane z Identity wyjątkiem, który rozszerza ApiAuthorizationDbContext
(bardziej pochodną klasę z IdentityDbContext
) w celu uwzględnienia schematu dla klasy IdentityServer.
Aby uzyskać pełną kontrolę nad schematem bazy danych, dziedziczyć z jednej z dostępnych IdentityDbContext
klas i skonfigurować kontekst, aby uwzględnić Identity schemat przez wywołanie builder.ConfigurePersistedGrantContext(_operationalStoreOptions.Value)
OnModelCreating
metody .
OidcConfigurationController
W pliku zwróć uwagę na punkt końcowy, który jest aprowizowany w celu obsługi parametrów OIDC, których klient musi użyć.
appsettings.json
appsettings.json
W pliku głównym projektu znajduje się nowa IdentityServer
sekcja, która opisuje listę skonfigurowanych klientów. W poniższym przykładzie istnieje jeden klient. Nazwa klienta odpowiada nazwie aplikacji i jest mapowana zgodnie z konwencją do parametru OAuth ClientId
. Profil wskazuje skonfigurowany typ aplikacji. Jest ona używana wewnętrznie do napędzania konwencji, które upraszczają proces konfiguracji serwera. Istnieje kilka dostępnych profilów, jak wyjaśniono w sekcji Profile aplikacji.
"IdentityServer": {
"Clients": {
"angularindividualpreview3final": {
"Profile": "IdentityServerSPA"
}
}
}
appsettings.Development.json
appsettings.Development.json
W pliku głównym projektu znajduje się IdentityServer
sekcja opisujący klucz używany do podpisywania tokenów. Podczas wdrażania w środowisku produkcyjnym należy aprowizować i wdrażać klucz obok aplikacji, jak wyjaśniono w sekcji Wdrażanie w środowisku produkcyjnym .
"IdentityServer": {
"Key": {
"Type": "Development"
}
}
Ogólny opis aplikacji Angular
Obsługa uwierzytelniania i autoryzacji interfejsu API w szablonie usługi Angular znajduje się we własnym module Angular w katalogu ClientApp/src/api-authorization . Moduł składa się z następujących elementów:
- 3 składniki:
login.component.ts
: obsługuje przepływ logowania aplikacji.logout.component.ts
: obsługuje przepływ wylogowywanie aplikacji.login-menu.component.ts
: widżet, który wyświetla jeden z następujących zestawów łączy:- Zarządzanie profilami użytkowników i wylogowywanie linków podczas uwierzytelniania użytkownika.
- Rejestracja i logowanie linków, gdy użytkownik nie jest uwierzytelniony.
- Zabezpieczenie
AuthorizeGuard
trasy, które można dodać do tras i wymaga uwierzytelnienia użytkownika przed przejściem do trasy. - Przechwytownik
AuthorizeInterceptor
HTTP, który dołącza token dostępu do wychodzących żądań HTTP przeznaczonych dla interfejsu API podczas uwierzytelniania użytkownika. - Usługa
AuthorizeService
, która obsługuje szczegóły niższego poziomu procesu uwierzytelniania i uwidacznia informacje o uwierzytelnianym użytkowniku rest w aplikacji do użycia. - Moduł Angular, który definiuje trasy skojarzone z częściami uwierzytelniania aplikacji. Uwidacznia składnik menu logowania, przechwytnik, zabezpieczenie i usługę do użycia z rest aplikacji.
Ogólny opis aplikacji React
Obsługa uwierzytelniania i autoryzacji interfejsu API w szablonie react znajduje się w katalogu ClientApp/src/components/api-authorization . Składa się z następujących elementów:
- 4 składniki:
Login.js
: obsługuje przepływ logowania aplikacji.Logout.js
: obsługuje przepływ wylogowywanie aplikacji.LoginMenu.js
: widżet, który wyświetla jeden z następujących zestawów łączy:- Zarządzanie profilami użytkowników i wylogowywanie linków podczas uwierzytelniania użytkownika.
- Rejestracja i logowanie linków, gdy użytkownik nie jest uwierzytelniony.
AuthorizeRoute.js
: składnik trasy, który wymaga uwierzytelnienia użytkownika przed renderowaniem składnika wskazanego w parametrzeComponent
.
- Wyeksportowane
authService
wystąpienie klasyAuthorizeService
obsługujące szczegóły niższego poziomu procesu uwierzytelniania i uwidaczniające informacje o uwierzytelnianym użytkowniku rest w aplikacji do użycia.
Po zapoznaniu się z głównymi składnikami rozwiązania możesz dokładniej przyjrzeć się poszczególnym scenariuszom aplikacji.
Wymaganie autoryzacji w nowym interfejsie API
Domyślnie system jest skonfigurowany tak, aby można było łatwo wymagać autoryzacji dla nowych interfejsów API. W tym celu utwórz nowy kontroler i dodaj [Authorize]
atrybut do klasy kontrolera lub dowolnej akcji w obrębie kontrolera.
Dostosowywanie procedury obsługi uwierzytelniania interfejsu API
Aby dostosować konfigurację programu obsługi JWT interfejsu API, skonfiguruj jego JwtBearerOptions wystąpienie:
builder.Services.AddAuthentication()
.AddIdentityServerJwt();
builder.Services.Configure<JwtBearerOptions>(
IdentityServerJwtConstants.IdentityServerJwtBearerScheme,
options =>
{
...
});
Procedura obsługi JWT interfejsu API zgłasza zdarzenia, które umożliwiają kontrolę nad procesem uwierzytelniania przy użyciu polecenia JwtBearerEvents
. Aby zapewnić obsługę autoryzacji interfejsu API, AddIdentityServerJwt
rejestruje własne programy obsługi zdarzeń.
Aby dostosować obsługę zdarzenia, zawijaj istniejącą procedurę obsługi zdarzeń z dodatkową logiką zgodnie z potrzebami. Na przykład:
builder.Services.Configure<JwtBearerOptions>(
IdentityServerJwtConstants.IdentityServerJwtBearerScheme,
options =>
{
var onTokenValidated = options.Events.OnTokenValidated;
options.Events.OnTokenValidated = async context =>
{
await onTokenValidated(context);
...
}
});
W poprzednim kodzie OnTokenValidated
program obsługi zdarzeń jest zastępowany implementacją niestandardową. Ta implementacja:
- Wywołuje oryginalną implementację zapewnianą przez obsługę autoryzacji interfejsu API.
- Uruchamianie własnej logiki niestandardowej.
Ochrona trasy po stronie klienta (Angular)
Ochrona trasy po stronie klienta jest wykonywana przez dodanie ochrony autoryzacji do listy zabezpieczeń do uruchomienia podczas konfigurowania trasy. Na przykład możesz zobaczyć, jak fetch-data
trasa jest skonfigurowana w głównym module angular aplikacji:
RouterModule.forRoot([
// ...
{ path: 'fetch-data', component: FetchDataComponent, canActivate: [AuthorizeGuard] },
])
Należy pamiętać, że ochrona trasy nie chroni rzeczywistego punktu końcowego (który nadal wymaga zastosowanego [Authorize]
atrybutu), ale uniemożliwia użytkownikowi przejście do danej trasy po stronie klienta, gdy nie jest uwierzytelniony.
Uwierzytelnianie żądań interfejsu API (Angular)
Uwierzytelnianie żądań do interfejsów API hostowanych razem z aplikacją odbywa się automatycznie za pomocą przechwytnika klienta HTTP zdefiniowanego przez aplikację.
Ochrona trasy po stronie klienta (React)
Chroń trasę po stronie klienta przy użyciu AuthorizeRoute
składnika zamiast zwykłego Route
składnika. Zwróć na przykład uwagę na to, jak fetch-data
trasa jest skonfigurowana w składniku App
:
<AuthorizeRoute path='/fetch-data' component={FetchData} />
Ochrona trasy:
- Nie chroni rzeczywistego punktu końcowego (który nadal wymaga zastosowanego atrybutu
[Authorize]
). - Uniemożliwia użytkownikowi przejście do danej trasy po stronie klienta tylko wtedy, gdy nie zostanie uwierzytelniony.
Uwierzytelnianie żądań interfejsu API (React)
Uwierzytelnianie żądań za pomocą platformy React odbywa się najpierw przez zaimportowanie authService
wystąpienia z klasy AuthorizeService
. Token dostępu jest pobierany z authService
obiektu i jest dołączony do żądania, jak pokazano poniżej. W składnikach react ta praca jest zwykle wykonywana w componentDidMount
metodzie cyklu życia lub w wyniku interakcji z użytkownikiem.
Importowanie elementu authService
do składnika
import authService from './api-authorization/AuthorizeService'
Pobieranie i dołączanie tokenu dostępu do odpowiedzi
async populateWeatherData() {
const token = await authService.getAccessToken();
const response = await fetch('api/SampleData/WeatherForecasts', {
headers: !token ? {} : { 'Authorization': `Bearer ${token}` }
});
const data = await response.json();
this.setState({ forecasts: data, loading: false });
}
Wdróż w środowisku produkcyjnym
Aby wdrożyć aplikację w środowisku produkcyjnym, należy aprowizować następujące zasoby:
- Baza danych do przechowywania Identity kont użytkowników i grantów IdentityServer.
- Certyfikat produkcyjny do użycia na potrzeby tokenów podpisywania.
- Nie ma żadnych konkretnych wymagań dotyczących tego certyfikatu; może to być certyfikat z podpisem własnym lub certyfikat aprowizowany za pośrednictwem urzędu certyfikacji.
- Można go wygenerować za pomocą standardowych narzędzi, takich jak Program PowerShell lub OpenSSL.
- Można go zainstalować w magazynie certyfikatów na maszynach docelowych lub wdrożyć jako plik pfx z silnym hasłem.
Przykład: wdrażanie u dostawcy hostingu internetowego spoza platformy Azure
W panelu hostingu internetowego utwórz lub załaduj certyfikat. Następnie w pliku aplikacji appsettings.json
zmodyfikuj IdentityServer
sekcję w celu uwzględnienia kluczowych szczegółów. Na przykład:
"IdentityServer": {
"Key": {
"Type": "Store",
"StoreName": "WebHosting",
"StoreLocation": "CurrentUser",
"Name": "CN=MyApplication"
}
}
W powyższym przykładzie:
StoreName
reprezentuje nazwę magazynu certyfikatów, w którym jest przechowywany certyfikat. W takim przypadku wskazuje on sklep hostingu internetowego.StoreLocation
reprezentuje miejsce ładowania certyfikatu z (CurrentUser
w tym przypadku).Name
odnosi się do podmiotu wyróżniającego certyfikatu.
Przykład: Wdrażanie w usłudze aplikacja systemu Azure
W tej sekcji opisano wdrażanie aplikacji w usłudze aplikacja systemu Azure przy użyciu certyfikatu przechowywanego w magazynie certyfikatów. Aby zmodyfikować aplikację w celu załadowania certyfikatu z magazynu certyfikatów, wymagany jest plan usługi warstwy Standardowa lub lepszy podczas konfigurowania aplikacji w witrynie Azure Portal w późniejszym kroku.
W pliku aplikacji appsettings.json
zmodyfikuj IdentityServer
sekcję, aby zawierała kluczowe szczegóły:
"IdentityServer": {
"Key": {
"Type": "Store",
"StoreName": "My",
"StoreLocation": "CurrentUser",
"Name": "CN=MyApplication"
}
}
- Nazwa magazynu reprezentuje nazwę magazynu certyfikatów, w którym jest przechowywany certyfikat. W takim przypadku wskazuje magazyn użytkowników osobistych.
- Lokalizacja magazynu reprezentuje miejsce ładowania certyfikatu z (
CurrentUser
lubLocalMachine
). - Właściwość name certyfikatu odpowiada wyróżniającej podmiotowi certyfikatu.
Aby wdrożyć aplikację w usłudze aplikacja systemu Azure Service, wykonaj kroki opisane w artykule Wdrażanie aplikacji na platformie Azure, w którym wyjaśniono, jak utworzyć niezbędne zasoby platformy Azure i wdrożyć aplikację w środowisku produkcyjnym.
Po wykonaniu powyższych instrukcji aplikacja zostanie wdrożona na platformie Azure, ale nie jest jeszcze funkcjonalna. Certyfikat używany przez aplikację musi być skonfigurowany w witrynie Azure Portal. Znajdź odcisk palca certyfikatu i wykonaj kroki opisane w temacie Ładowanie certyfikatów.
Chociaż te kroki obejmują protokół SSL, w witrynie Azure Portal znajduje się sekcja Certyfikaty prywatne, w której można przekazać aprowizowany certyfikat do użycia z aplikacją.
Po skonfigurowaniu aplikacji i ustawień aplikacji w witrynie Azure Portal uruchom ponownie aplikację w portalu.
Inne opcje konfiguracji
Obsługa autoryzacji interfejsu API opiera się na maszynie wirtualnej IdentityServer z zestawem konwencji, wartości domyślnych i ulepszeń, aby uprościć środowisko dla spAs. Nie trzeba dodawać, że pełna moc maszyny wirtualnej IdentityServer jest dostępna w tle, jeśli integracja ASP.NET Core nie obejmuje twojego scenariusza. Obsługa platformy ASP.NET Core koncentruje się na aplikacjach "firmowych", w których wszystkie aplikacje są tworzone i wdrażane przez naszą organizację. W związku z tym pomoc techniczna nie jest oferowana w przypadku takich rzeczy, jak zgoda lub federacja. W tych scenariuszach użyj maszyny wirtualnej IdentityServer i postępuj zgodnie z ich dokumentacją.
Profile aplikacji
Profile aplikacji to wstępnie zdefiniowane konfiguracje aplikacji, które dodatkowo definiują ich parametry. Obecnie obsługiwane są następujące profile:
IdentityServerSPA
: reprezentuje SPA hostowany obok IdentityServer jako pojedynczą jednostkę.- Wartość domyślna
redirect_uri
to/authentication/login-callback
. - Wartość domyślna
post_logout_redirect_uri
to/authentication/logout-callback
. - Zestaw zakresów obejmuje
openid
zakresy ,profile
i każdy zakres zdefiniowany dla interfejsów API w aplikacji. - Zestaw dozwolonych typów odpowiedzi OIDC jest
id_token token
lub każdy z nich indywidualnie (id_token
,token
). - Dozwolony tryb odpowiedzi to
fragment
.
- Wartość domyślna
SPA
: reprezentuje SPA, który nie jest hostowany w usłudze IdentityServer.- Zestaw zakresów obejmuje
openid
zakresy ,profile
i każdy zakres zdefiniowany dla interfejsów API w aplikacji. - Zestaw dozwolonych typów odpowiedzi OIDC jest
id_token token
lub każdy z nich indywidualnie (id_token
,token
). - Dozwolony tryb odpowiedzi to
fragment
.
- Zestaw zakresów obejmuje
IdentityServerJwt
: reprezentuje interfejs API hostowany razem z usługą IdentityServer.- Aplikacja jest skonfigurowana tak, aby miała jeden zakres, który domyślnie ma nazwę aplikacji.
API
: reprezentuje interfejs API, który nie jest hostowany w usłudze IdentityServer.- Aplikacja jest skonfigurowana tak, aby miała jeden zakres, który domyślnie ma nazwę aplikacji.
Konfiguracja za pośrednictwem AppSettings
Skonfiguruj aplikacje za pomocą systemu konfiguracji, dodając je do listy Clients
elementów lub Resources
.
Skonfiguruj właściwości i post_logout_redirect_uri
klientaredirect_uri
, jak pokazano w poniższym przykładzie:
"IdentityServer": {
"Clients": {
"MySPA": {
"Profile": "SPA",
"RedirectUri": "https://www.example.com/authentication/login-callback",
"LogoutUri": "https://www.example.com/authentication/logout-callback"
}
}
}
Podczas konfigurowania zasobów można skonfigurować zakresy dla zasobu, jak pokazano poniżej:
"IdentityServer": {
"Resources": {
"MyExternalApi": {
"Profile": "API",
"Scopes": "a b c"
}
}
}
Konfiguracja za pomocą kodu
Można również skonfigurować klientów i zasoby za pomocą kodu przy użyciu przeciążenia AddApiAuthorization
, które podejmuje akcję w celu skonfigurowania opcji.
AddApiAuthorization<ApplicationUser, ApplicationDbContext>(options =>
{
options.Clients.AddSPA(
"My SPA", spa =>
spa.WithRedirectUri("http://www.example.com/authentication/login-callback")
.WithLogoutRedirectUri(
"http://www.example.com/authentication/logout-callback"));
options.ApiResources.AddApiResource("MyExternalApi", resource =>
resource.WithScopes("a", "b", "c"));
});
Dodatkowe zasoby
Szablony ASP.NET Core 3.1 i nowszych oferują uwierzytelnianie w aplikacjach jednostronicowych (SPA) przy użyciu obsługi autoryzacji interfejsu API. ASP.NET Core Identity na potrzeby uwierzytelniania i przechowywania użytkowników jest łączona z usługą IdentityServer na potrzeby implementowania programu OpenID Connect.
Parametr uwierzytelniania został dodany do szablonów projektów Angular i React , które są podobne do parametru uwierzytelniania w szablonach projektów Aplikacji internetowej (Model-View-Controller) i Aplikacji internetowej (Razor Pages). Dozwolone wartości parametrów to None i Individual. Szablon projektu React.js i Redux nie obsługuje obecnie parametru uwierzytelniania.
Tworzenie aplikacji z obsługą autoryzacji interfejsu API
Uwierzytelnianie i autoryzacja użytkownika mogą być używane zarówno z usługami Angular, jak i React SPA. Otwórz powłokę poleceń i uruchom następujące polecenie:
Angular:
dotnet new angular -o <output_directory_name>
React:
dotnet new react -o <output_directory_name> -au Individual
Poprzednie polecenie tworzy aplikację ASP.NET Core z katalogiem ClientApp zawierającym SPA.
Ogólny opis składników ASP.NET Core aplikacji
W poniższych sekcjach opisano dodatki do projektu, gdy jest uwzględniona obsługa uwierzytelniania:
Klasa Startup
Poniższe przykłady kodu bazują na pakiecie NuGet Microsoft.AspNetCore.ApiAuthorization.IdentityServer . Przykłady umożliwiają skonfigurowanie uwierzytelniania i autoryzacji interfejsu AddApiAuthorization API przy użyciu metod i AddIdentityServerJwt rozszerzeń. Projekty korzystające z szablonów projektów React lub Angular SPA z uwierzytelnianiem obejmują odwołanie do tego pakietu.
Klasa Startup
ma następujące dodatki:
Startup.ConfigureServices
Wewnątrz metody:Identity z domyślnym interfejsem użytkownika:
services.AddDbContext<ApplicationDbContext>(options => options.UseSqlite(Configuration.GetConnectionString("DefaultConnection"))); services.AddDefaultIdentity<ApplicationUser>() .AddEntityFrameworkStores<ApplicationDbContext>();
IdentityServer z dodatkową
AddApiAuthorization
metodą pomocnika, która konfiguruje niektóre domyślne konwencje ASP.NET Core na serwerze IdentityServer:services.AddIdentityServer() .AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
Uwierzytelnianie przy użyciu dodatkowej
AddIdentityServerJwt
metody pomocniczej, która konfiguruje aplikację do weryfikowania tokenów JWT utworzonych przez usługę IdentityServer:services.AddAuthentication() .AddIdentityServerJwt();
Startup.Configure
Wewnątrz metody:Oprogramowanie pośredniczące uwierzytelniania, które jest odpowiedzialne za weryfikowanie poświadczeń żądania i ustawianie użytkownika w kontekście żądania:
app.UseAuthentication();
Oprogramowanie pośredniczące IdentityServer, które uwidacznia punkty końcowe OpenID Connect:
app.UseIdentityServer();
Ostrzeżenie
W tym artykule przedstawiono użycie parametry połączenia. W przypadku lokalnej bazy danych użytkownik nie musi być uwierzytelniany, ale w środowisku produkcyjnym parametry połączenia czasami zawiera hasło do uwierzytelniania. Poświadczenie hasła właściciela zasobu (ROPC) jest zagrożeniem bezpieczeństwa, którego należy unikać w produkcyjnych bazach danych. Aplikacje produkcyjne powinny korzystać z najbezpieczniejszego dostępnego przepływu uwierzytelniania. Aby uzyskać więcej informacji na temat uwierzytelniania aplikacji wdrożonych w środowiskach testowych lub produkcyjnych, zobacz Bezpieczne przepływy uwierzytelniania.
usługa aplikacja systemu Azure w systemie Linux
W przypadku wdrożeń usługi aplikacja systemu Azure w systemie Linux określ wystawcę jawnie w pliku Startup.ConfigureServices
:
services.Configure<JwtBearerOptions>(
IdentityServerJwtConstants.IdentityServerJwtBearerScheme,
options =>
{
options.Authority = "{AUTHORITY}";
});
W poprzednim kodzie {AUTHORITY}
symbol zastępczy jest Authority używany podczas wykonywania wywołań OpenID Connect.
Przykład:
options.Authority = "https://contoso-service.azurewebsites.net";
AddApiAuthorization
Ta metoda pomocnika konfiguruje maszynę wirtualną IdentityServer do korzystania z naszej obsługiwanej konfiguracji. IdentityServer to zaawansowana i rozszerzalna struktura do obsługi problemów z zabezpieczeniami aplikacji. Jednocześnie uwidacznia niepotrzebną złożoność dla najbardziej typowych scenariuszy. W związku z tym dostępny jest zestaw konwencji i opcji konfiguracji, które są uważane za dobry punkt wyjścia. Po zmianie potrzeb uwierzytelniania pełna moc serwera IdentityServer jest nadal dostępna w celu dostosowania uwierzytelniania zgodnie z potrzebami.
AddIdentityServerJwt
Ta metoda pomocnika konfiguruje schemat zasad dla aplikacji jako domyślną procedurę obsługi uwierzytelniania. Zasady są skonfigurowane tak, aby umożliwić Identity obsługę wszystkich żądań kierowanych do dowolnej ścieżki podrzędnej Identity w obszarze adresu URL "/Identity". Usługa JwtBearerHandler
obsługuje wszystkie inne żądania. Ponadto ta metoda rejestruje zasób interfejsu <<ApplicationName>>API
API za pomocą klasy IdentityServer z domyślnym zakresem <<ApplicationName>>API
i konfiguruje oprogramowanie pośredniczące tokenu elementu nośnego JWT w celu sprawdzania poprawności tokenów wystawionych przez usługę IdentityServer dla aplikacji.
WeatherForecastController
W pliku zwróć uwagę [Authorize]
na atrybut zastosowany do klasy, który wskazuje, że użytkownik musi być autoryzowany na podstawie domyślnych zasad dostępu do zasobu. Domyślne zasady autoryzacji mają być skonfigurowane tak, aby używały domyślnego schematu uwierzytelniania, który został skonfigurowany przez AddIdentityServerJwt
schemat zasad wymienionych powyżej, dzięki czemu JwtBearerHandler
skonfigurowana przez taką metodę pomocnika domyślna procedura obsługi żądań do aplikacji.
ApplicationDbContext
W pliku zwróć uwagę, że to samo DbContext
jest używane z Identity wyjątkiem, który rozszerza ApiAuthorizationDbContext
(bardziej pochodną klasę z IdentityDbContext
) w celu uwzględnienia schematu dla klasy IdentityServer.
Aby uzyskać pełną kontrolę nad schematem bazy danych, dziedziczyć z jednej z dostępnych IdentityDbContext
klas i skonfigurować kontekst, aby uwzględnić Identity schemat przez wywołanie builder.ConfigurePersistedGrantContext(_operationalStoreOptions.Value)
OnModelCreating
metody .
OidcConfigurationController
W pliku zwróć uwagę na punkt końcowy, który jest aprowizowany w celu obsługi parametrów OIDC, których klient musi użyć.
appsettings.json
appsettings.json
W pliku głównym projektu znajduje się nowa IdentityServer
sekcja, która opisuje listę skonfigurowanych klientów. W poniższym przykładzie istnieje jeden klient. Nazwa klienta odpowiada nazwie aplikacji i jest mapowana zgodnie z konwencją do parametru OAuth ClientId
. Profil wskazuje skonfigurowany typ aplikacji. Jest ona używana wewnętrznie do napędzania konwencji, które upraszczają proces konfiguracji serwera. Istnieje kilka dostępnych profilów, jak wyjaśniono w sekcji Profile aplikacji.
"IdentityServer": {
"Clients": {
"angularindividualpreview3final": {
"Profile": "IdentityServerSPA"
}
}
}
appsettings.Development.json
appsettings.Development.json
W pliku głównym projektu znajduje się IdentityServer
sekcja opisujący klucz używany do podpisywania tokenów. Podczas wdrażania w środowisku produkcyjnym należy aprowizować i wdrażać klucz obok aplikacji, jak wyjaśniono w sekcji Wdrażanie w środowisku produkcyjnym .
"IdentityServer": {
"Key": {
"Type": "Development"
}
}
Ogólny opis aplikacji Angular
Obsługa uwierzytelniania i autoryzacji interfejsu API w szablonie usługi Angular znajduje się we własnym module Angular w katalogu ClientApp/src/api-authorization . Moduł składa się z następujących elementów:
- 3 składniki:
login.component.ts
: obsługuje przepływ logowania aplikacji.logout.component.ts
: obsługuje przepływ wylogowywanie aplikacji.login-menu.component.ts
: widżet, który wyświetla jeden z następujących zestawów łączy:- Zarządzanie profilami użytkowników i wylogowywanie linków podczas uwierzytelniania użytkownika.
- Rejestracja i logowanie linków, gdy użytkownik nie jest uwierzytelniony.
- Zabezpieczenie
AuthorizeGuard
trasy, które można dodać do tras i wymaga uwierzytelnienia użytkownika przed przejściem do trasy. - Przechwytownik
AuthorizeInterceptor
HTTP, który dołącza token dostępu do wychodzących żądań HTTP przeznaczonych dla interfejsu API podczas uwierzytelniania użytkownika. - Usługa
AuthorizeService
, która obsługuje szczegóły niższego poziomu procesu uwierzytelniania i uwidacznia informacje o uwierzytelnianym użytkowniku rest w aplikacji do użycia. - Moduł Angular, który definiuje trasy skojarzone z częściami uwierzytelniania aplikacji. Uwidacznia składnik menu logowania, przechwytnik, zabezpieczenie i usługę do użycia z rest aplikacji.
Ogólny opis aplikacji React
Obsługa uwierzytelniania i autoryzacji interfejsu API w szablonie react znajduje się w katalogu ClientApp/src/components/api-authorization . Składa się z następujących elementów:
- 4 składniki:
Login.js
: obsługuje przepływ logowania aplikacji.Logout.js
: obsługuje przepływ wylogowywanie aplikacji.LoginMenu.js
: widżet, który wyświetla jeden z następujących zestawów łączy:- Zarządzanie profilami użytkowników i wylogowywanie linków podczas uwierzytelniania użytkownika.
- Rejestracja i logowanie linków, gdy użytkownik nie jest uwierzytelniony.
AuthorizeRoute.js
: składnik trasy, który wymaga uwierzytelnienia użytkownika przed renderowaniem składnika wskazanego w parametrzeComponent
.
- Wyeksportowane
authService
wystąpienie klasyAuthorizeService
obsługujące szczegóły niższego poziomu procesu uwierzytelniania i uwidaczniające informacje o uwierzytelnianym użytkowniku rest w aplikacji do użycia.
Po zapoznaniu się z głównymi składnikami rozwiązania możesz dokładniej przyjrzeć się poszczególnym scenariuszom aplikacji.
Wymaganie autoryzacji w nowym interfejsie API
Domyślnie system jest skonfigurowany tak, aby można było łatwo wymagać autoryzacji dla nowych interfejsów API. W tym celu utwórz nowy kontroler i dodaj [Authorize]
atrybut do klasy kontrolera lub dowolnej akcji w obrębie kontrolera.
Dostosowywanie procedury obsługi uwierzytelniania interfejsu API
Aby dostosować konfigurację programu obsługi JWT interfejsu API, skonfiguruj jego JwtBearerOptions wystąpienie:
services.AddAuthentication()
.AddIdentityServerJwt();
services.Configure<JwtBearerOptions>(
IdentityServerJwtConstants.IdentityServerJwtBearerScheme,
options =>
{
...
});
Procedura obsługi JWT interfejsu API zgłasza zdarzenia, które umożliwiają kontrolę nad procesem uwierzytelniania przy użyciu polecenia JwtBearerEvents
. Aby zapewnić obsługę autoryzacji interfejsu API, AddIdentityServerJwt
rejestruje własne programy obsługi zdarzeń.
Aby dostosować obsługę zdarzenia, zawijaj istniejącą procedurę obsługi zdarzeń z dodatkową logiką zgodnie z potrzebami. Na przykład:
services.Configure<JwtBearerOptions>(
IdentityServerJwtConstants.IdentityServerJwtBearerScheme,
options =>
{
var onTokenValidated = options.Events.OnTokenValidated;
options.Events.OnTokenValidated = async context =>
{
await onTokenValidated(context);
...
}
});
W poprzednim kodzie OnTokenValidated
program obsługi zdarzeń jest zastępowany implementacją niestandardową. Ta implementacja:
- Wywołuje oryginalną implementację zapewnianą przez obsługę autoryzacji interfejsu API.
- Uruchamianie własnej logiki niestandardowej.
Ochrona trasy po stronie klienta (Angular)
Ochrona trasy po stronie klienta jest wykonywana przez dodanie ochrony autoryzacji do listy zabezpieczeń do uruchomienia podczas konfigurowania trasy. Na przykład możesz zobaczyć, jak fetch-data
trasa jest skonfigurowana w głównym module angular aplikacji:
RouterModule.forRoot([
// ...
{ path: 'fetch-data', component: FetchDataComponent, canActivate: [AuthorizeGuard] },
])
Należy pamiętać, że ochrona trasy nie chroni rzeczywistego punktu końcowego (który nadal wymaga zastosowanego [Authorize]
atrybutu), ale uniemożliwia użytkownikowi przejście do danej trasy po stronie klienta, gdy nie jest uwierzytelniony.
Uwierzytelnianie żądań interfejsu API (Angular)
Uwierzytelnianie żądań do interfejsów API hostowanych razem z aplikacją odbywa się automatycznie za pomocą przechwytnika klienta HTTP zdefiniowanego przez aplikację.
Ochrona trasy po stronie klienta (React)
Chroń trasę po stronie klienta przy użyciu AuthorizeRoute
składnika zamiast zwykłego Route
składnika. Zwróć na przykład uwagę na to, jak fetch-data
trasa jest skonfigurowana w składniku App
:
<AuthorizeRoute path='/fetch-data' component={FetchData} />
Ochrona trasy:
- Nie chroni rzeczywistego punktu końcowego (który nadal wymaga zastosowanego atrybutu
[Authorize]
). - Uniemożliwia użytkownikowi przejście do danej trasy po stronie klienta tylko wtedy, gdy nie zostanie uwierzytelniony.
Uwierzytelnianie żądań interfejsu API (React)
Uwierzytelnianie żądań za pomocą platformy React odbywa się najpierw przez zaimportowanie authService
wystąpienia z klasy AuthorizeService
. Token dostępu jest pobierany z authService
obiektu i jest dołączony do żądania, jak pokazano poniżej. W składnikach react ta praca jest zwykle wykonywana w componentDidMount
metodzie cyklu życia lub w wyniku interakcji z użytkownikiem.
Importowanie elementu authService
do składnika
import authService from './api-authorization/AuthorizeService'
Pobieranie i dołączanie tokenu dostępu do odpowiedzi
async populateWeatherData() {
const token = await authService.getAccessToken();
const response = await fetch('api/SampleData/WeatherForecasts', {
headers: !token ? {} : { 'Authorization': `Bearer ${token}` }
});
const data = await response.json();
this.setState({ forecasts: data, loading: false });
}
Wdróż w środowisku produkcyjnym
Aby wdrożyć aplikację w środowisku produkcyjnym, należy aprowizować następujące zasoby:
- Baza danych do przechowywania Identity kont użytkowników i grantów IdentityServer.
- Certyfikat produkcyjny do użycia na potrzeby tokenów podpisywania.
- Nie ma żadnych konkretnych wymagań dotyczących tego certyfikatu; może to być certyfikat z podpisem własnym lub certyfikat aprowizowany za pośrednictwem urzędu certyfikacji.
- Można go wygenerować za pomocą standardowych narzędzi, takich jak Program PowerShell lub OpenSSL.
- Można go zainstalować w magazynie certyfikatów na maszynach docelowych lub wdrożyć jako plik pfx z silnym hasłem.
Przykład: wdrażanie u dostawcy hostingu internetowego spoza platformy Azure
W panelu hostingu internetowego utwórz lub załaduj certyfikat. Następnie w pliku aplikacji appsettings.json
zmodyfikuj IdentityServer
sekcję w celu uwzględnienia kluczowych szczegółów. Na przykład:
"IdentityServer": {
"Key": {
"Type": "Store",
"StoreName": "WebHosting",
"StoreLocation": "CurrentUser",
"Name": "CN=MyApplication"
}
}
W powyższym przykładzie:
StoreName
reprezentuje nazwę magazynu certyfikatów, w którym jest przechowywany certyfikat. W takim przypadku wskazuje on sklep hostingu internetowego.StoreLocation
reprezentuje miejsce ładowania certyfikatu z (CurrentUser
w tym przypadku).Name
odnosi się do podmiotu wyróżniającego certyfikatu.
Przykład: Wdrażanie w usłudze aplikacja systemu Azure
W tej sekcji opisano wdrażanie aplikacji w usłudze aplikacja systemu Azure przy użyciu certyfikatu przechowywanego w magazynie certyfikatów. Aby zmodyfikować aplikację w celu załadowania certyfikatu z magazynu certyfikatów, wymagany jest plan usługi warstwy Standardowa lub lepszy podczas konfigurowania aplikacji w witrynie Azure Portal w późniejszym kroku.
W pliku aplikacji appsettings.json
zmodyfikuj IdentityServer
sekcję, aby zawierała kluczowe szczegóły:
"IdentityServer": {
"Key": {
"Type": "Store",
"StoreName": "My",
"StoreLocation": "CurrentUser",
"Name": "CN=MyApplication"
}
}
- Nazwa magazynu reprezentuje nazwę magazynu certyfikatów, w którym jest przechowywany certyfikat. W takim przypadku wskazuje magazyn użytkowników osobistych.
- Lokalizacja magazynu reprezentuje miejsce ładowania certyfikatu z (
CurrentUser
lubLocalMachine
). - Właściwość name certyfikatu odpowiada wyróżniającej podmiotowi certyfikatu.
Aby wdrożyć aplikację w usłudze aplikacja systemu Azure Service, wykonaj kroki opisane w artykule Wdrażanie aplikacji na platformie Azure, w którym wyjaśniono, jak utworzyć niezbędne zasoby platformy Azure i wdrożyć aplikację w środowisku produkcyjnym.
Po wykonaniu powyższych instrukcji aplikacja zostanie wdrożona na platformie Azure, ale nie jest jeszcze funkcjonalna. Certyfikat używany przez aplikację musi być skonfigurowany w witrynie Azure Portal. Znajdź odcisk palca certyfikatu i wykonaj kroki opisane w temacie Ładowanie certyfikatów.
Chociaż te kroki obejmują protokół SSL, w witrynie Azure Portal znajduje się sekcja Certyfikaty prywatne, w której można przekazać aprowizowany certyfikat do użycia z aplikacją.
Po skonfigurowaniu aplikacji i ustawień aplikacji w witrynie Azure Portal uruchom ponownie aplikację w portalu.
Inne opcje konfiguracji
Obsługa autoryzacji interfejsu API opiera się na maszynie wirtualnej IdentityServer z zestawem konwencji, wartości domyślnych i ulepszeń, aby uprościć środowisko dla spAs. Nie trzeba dodawać, że pełna moc maszyny wirtualnej IdentityServer jest dostępna w tle, jeśli integracja ASP.NET Core nie obejmuje twojego scenariusza. Obsługa platformy ASP.NET Core koncentruje się na aplikacjach "firmowych", w których wszystkie aplikacje są tworzone i wdrażane przez naszą organizację. W związku z tym pomoc techniczna nie jest oferowana w przypadku takich rzeczy, jak zgoda lub federacja. W tych scenariuszach użyj maszyny wirtualnej IdentityServer i postępuj zgodnie z ich dokumentacją.
Profile aplikacji
Profile aplikacji to wstępnie zdefiniowane konfiguracje aplikacji, które dodatkowo definiują ich parametry. Obecnie obsługiwane są następujące profile:
IdentityServerSPA
: reprezentuje SPA hostowany obok IdentityServer jako pojedynczą jednostkę.- Wartość domyślna
redirect_uri
to/authentication/login-callback
. - Wartość domyślna
post_logout_redirect_uri
to/authentication/logout-callback
. - Zestaw zakresów obejmuje
openid
zakresy ,profile
i każdy zakres zdefiniowany dla interfejsów API w aplikacji. - Zestaw dozwolonych typów odpowiedzi OIDC jest
id_token token
lub każdy z nich indywidualnie (id_token
,token
). - Dozwolony tryb odpowiedzi to
fragment
.
- Wartość domyślna
SPA
: reprezentuje SPA, który nie jest hostowany w usłudze IdentityServer.- Zestaw zakresów obejmuje
openid
zakresy ,profile
i każdy zakres zdefiniowany dla interfejsów API w aplikacji. - Zestaw dozwolonych typów odpowiedzi OIDC jest
id_token token
lub każdy z nich indywidualnie (id_token
,token
). - Dozwolony tryb odpowiedzi to
fragment
.
- Zestaw zakresów obejmuje
IdentityServerJwt
: reprezentuje interfejs API hostowany razem z usługą IdentityServer.- Aplikacja jest skonfigurowana tak, aby miała jeden zakres, który domyślnie ma nazwę aplikacji.
API
: reprezentuje interfejs API, który nie jest hostowany w usłudze IdentityServer.- Aplikacja jest skonfigurowana tak, aby miała jeden zakres, który domyślnie ma nazwę aplikacji.
Konfiguracja za pośrednictwem AppSettings
Skonfiguruj aplikacje za pomocą systemu konfiguracji, dodając je do listy Clients
elementów lub Resources
.
Skonfiguruj właściwości i post_logout_redirect_uri
klientaredirect_uri
, jak pokazano w poniższym przykładzie:
"IdentityServer": {
"Clients": {
"MySPA": {
"Profile": "SPA",
"RedirectUri": "https://www.example.com/authentication/login-callback",
"LogoutUri": "https://www.example.com/authentication/logout-callback"
}
}
}
Podczas konfigurowania zasobów można skonfigurować zakresy dla zasobu, jak pokazano poniżej:
"IdentityServer": {
"Resources": {
"MyExternalApi": {
"Profile": "API",
"Scopes": "a b c"
}
}
}
Konfiguracja za pomocą kodu
Można również skonfigurować klientów i zasoby za pomocą kodu przy użyciu przeciążenia AddApiAuthorization
, które podejmuje akcję w celu skonfigurowania opcji.
AddApiAuthorization<ApplicationUser, ApplicationDbContext>(options =>
{
options.Clients.AddSPA(
"My SPA", spa =>
spa.WithRedirectUri("http://www.example.com/authentication/login-callback")
.WithLogoutRedirectUri(
"http://www.example.com/authentication/logout-callback"));
options.ApiResources.AddApiResource("MyExternalApi", resource =>
resource.WithScopes("a", "b", "c"));
});