Generierung von Client APIs mit Swagger Teil 2

Mit jedem neuen Backend-Release müssen Frontend-Entwickler*innen große Teile des API-Clients neu schreiben. Was wäre, wenn es eine Lösung gäbe, um den gesamten Client in nur wenigen Sekunden zu aktualisieren?

Also available in English at Medium.com.

In diesem Blog-Post stellen wir die Client-Generierung von Swagger vor, zeigen, wie man die generierte API in React-Anwendungen verwendet und wie man das Authentifizierungs-Token von Auth0 integriert.

Der Einfachheit halber wird dieses Thema in eine dreiteilige Serie aufgeteilt:

  1. Teil 1: Integration von Swagger in Spring Boot, Generierung des API-Clients und Verwendung als Teil einer React-Web-App
  2. Teil 2: Sicherung der Endpunkte mit Auth0 und Autorisierung der Anfragen der Web-Clients
  3. Teil 3: Verwendung des generierten API-Clients als Teil einer React Native App.

Sichern der Endpoints mit Auth0 und Autorisierung der Webclient-Anfragen

Im zweiten Teil unserer Blogpost-Serie sichern wir unsere Web-Anwendung mit OAuth2 unter Verwendung von Auth0. Auth0 nennt sich selbst "eine einfach zu implementierende, anpassungsfähige Authentifizierungs- und Autorisierungsplattform" und bietet einen kostenlosen Tarif für bis zu 7000 Benutzer an. In diesem Beitrag wird die Integration von Auth0 in das Spring Boot Backend und die React-Webanwendung behandelt.

TL;DR

  1. Erstellen Sie einen neuen Tenant in Auth0, wie in Schritt 1 beschrieben.
  2. Klonen Sie unser Beispiel-Backend und starten Sie die Docker-Services.
  3. Setzen Sie in der src/main/resources/application.properties den Wert spring.security.oauth2.resourceserver.jwt.issuer-uri=https://<tenant>.eu.auth0.com, um den neuen Tenant zu nutzen.
  4. Starten Sie das Backend ohne no-security-Profil.
  5. Klonen Sie unsere Gesicherte Beispiel-Web-App und installieren Sie alle Abhängigkeiten mit yarn install.
  6. Setzen Sie die folgenden Umgebungsvariablen in der Run-Konfiguration der Web-Anwendung:

    REACT_APP_DOMAIN=<IHRE_DOMAIN> (z.B.: https://miragon-example-tenant.eu.auth0.com);
    REACT_APP_CLIENT_ID=<IHRE_CLIENT_ID> (z.B.: yXu7adklfU729Hgsdg93p0gag9dC);
    
  7. Starten Sie die Anwendung mit yarn start.

Schritt 1) Erstellen eines Auth0-Kontos, Anlegen eines neuen Tenants und einer neuen Anwendung.

  1. Besuchen Sie Auth0 und erstellen Sie ein neues Konto: https://auth0.com/.
  2. Erstellen Sie einen neuen Tenant: Klicken Sie auf Ihren Default-Tenant und wählen Sie "Create Tenant".
  3. Erstellen Sie eine neue Anwendung: Anwendungen → "Create Application" → "Single Web Page Applications".
  4. Konfigurieren Sie Ihre Anwendung: Ändern Sie den Namen und setzen Sie "Allowed Callback URLs" sowie "Allowed Web Origins" auf "http://localhost:8080/" und speichern Sie die Änderungen (den Button finden Sie am unteren Rand des Formulars").
  5. Erstellen Sie einen Benutzer: User Management → Users → Create User

Schritt 2) Backend-Anwendung sichern

Setzen Sie in der src/main/resources/application.properties den Wert spring.security.oauth2.resourceserver.jwt.issuer-uri=<AUTH0_TENANT_DOMAIN>, um Ihren Auth0-Tenant zu verwenden. Die Domain kann auf der Anwendungsseite in Auth0 gefunden werden.

Sie können Ihr Backend mit der folgenden Security-Konfiguration schützen (Sie finden diese Klasse im Beispiel-Backend im Ordner shared/security):

@Profile("!no-security")
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    public void configure(final HttpSecurity http) throws Exception {

        http.authorizeRequests()
                .antMatchers("/actuator/health").permitAll()
                .antMatchers("/v3/api-docs/**",
                        "/swagger-resources",
                        "/swagger-resources/**",
                        "/configuration/ui",
                        "/configuration/security",
                        "/swagger-ui.html",
                        "/swagger-ui/**",
                        "/webjars/**").permitAll()
                .anyRequest()
                .authenticated()
                .and()
                .oauth2ResourceServer(
                        OAuth2ResourceServerConfigurer::jwt
                );

        http.headers().frameOptions().sameOrigin();
        http.csrf().disable();
        http.sessionManagement()
                .sessionCreationPolicy(
                        SessionCreationPolicy.STATELESS
                );
    }

    @Bean
    public JwtDecoder jwtDecoderByIssuerUri(
        final OAuth2ResourceServerProperties properties
    ) {
        final String issuerUri = properties.getJwt().getIssuerUri();
        return JwtDecoders.fromIssuerLocation(issuerUri);
    }
}

Schritt 3) Web-App sichern

Wir werden Auth0s Benutzername & Passwort-Authentifizierung verwenden, die erscheinen sollte, wenn der Benutzer nicht angemeldet ist.

Der Login-Screen von Auth0:

3a) Hinzufügen des Auth0-Providers

Installieren Sie die erforderliche Abhängigkeit über yarn add @auth0/auth0-react und fügen Sie den Provider in Ihre Anwendung ein (index.tsx):

<Auth0Provider
    domain={process.env.REACT_APP_DOMAIN ?? ""}
    clientId={process.env.REACT_APP_CLIENT_ID ?? ""}
    authorizeTimeoutSeconds={5}
    useRefreshTokensIn
    redirectUri={window.location.origin}>
    <HashRouter>
        <App/>
    </HashRouter>
</Auth0Provider>

Die Domain und die Client-ID finden Sie in der Auth0-Anwendung, die Sie oben erstellt haben.

3b) Speichern des Tokens im Local Browser Storage:

Wenn sich der Benutzer erfolgreich angemeldet hat, können wir nun das JWT-Token im lokalen Browser-Speicher ablegen, um von jeder Seite aus darauf zuzugreifen (src/components/Layout/Layout.tsx):

(async () => {
    const token = await getIdTokenClaims();
    localStorage.setItem("token", token.__raw)
})();

Der Token im lokalen Browser-Speicher:

3c) Konfiguration der Swagger-API

Verwenden Sie die Configuration-Klasse, um den Authentifizierungsheader zu setzen (src/constants/Functions.tsx):

getClientConfig: function (): Configuration {
    return new Configuration({
        baseOptions: {
            "headers": {
                'Authorization': `Bearer ${this.getBearerToken()}`,
            }
        }
    })
},
getBearerToken: function (): string | null {
    return localStorage.getItem("token");
}

3d) Konfiguration beim Erstellen eines API-Client setzen.

Mit dieser Konfiguration können Sie jetzt authentifizierte Anfragen an die gesicherten Endpoints Ihres Backends senden:

const config = getClientConfig();
const projectController = new ProjectControllerApi(config)
const response = await projectController.getAllProject()
const data = response.data

Im dritten - und letzten - Teil dieser Serie werden wir einen genaueren Blick darauf werfen, wie man den generierten API-Client als Teil einer React Native App verwendet. Bis zum nächsten Mal!