Risorse per sviluppatori

Identita standards-first in pochi minuti.

Inizia con login OIDC standard e account self-service familiari. Aggiungi profili gestiti solo quando il tuo prodotto ha bisogno di persone a carico, gestione delegata o continuita del profilo.

Usa prima l'OIDC standard. Aggiungi profili gestiti solo quando il modello del prodotto lo richiede.
manage-tuurio-id

Template di identita gratuiti e pronti per la produzione con Tuurio ID

Esegui un flusso CLI per creare un tenant, provisionare i client e scaricare sample pronti con .env precompilato.

Comando di avvio rapido ~5 minuti
npx manage-tuurio-id@latest
Cosa fa la CLI
  1. Scegli Login, New o Website.
  2. New crea tenant + admin e scambia automaticamente il token.
  3. Crea client SPA o client web lato server.
  4. Abilita opzionalmente i webhook per i template server-side.
  5. Scarica il sample GitHub corrispondente e scrive .env automaticamente.
Template inclusi
React SPA Vue SPA Angular SPA Node.js Python Java / Spring Go PHP

Per template server-side con webhook: prima deploy, poi aggiorna l'URL endpoint webhook nella pagina admin webhook del tenant.

Integrazioni di esempio

Parti da un sample funzionante, non da uno snippet astratto

Il repository pubblico auth_samples offre app di riferimento per associazioni, scuole, portali membri e strumenti interni. Il codice resta su GitHub; questa pagina e il punto di ingresso per gli stack che il tuo team consegna davvero.

Apri repository completo

Web SPA

Sample per client pubblici con React, Vue 3, Angular e Next.js con Authorization Code + PKCE.

Web app server-side

Sample per client confidenziali e applicazioni basate su sessione in Node.js, Python, Java, Go, PHP e Laravel.

Mobile e native

Sample Android, iOS e Flutter per flussi di accesso con deep link e app link.

Scegli il tuo stack

Tutti gli esempi sono vicini alla produzione
server.js (Express + express-openid-connect) npm install express-openid-connect

const { auth } = require('express-openid-connect');

const config = {
  authRequired: false,
  auth0Logout: true,
  secret: 'YOUR_LONG_RANDOM_STRING',
  baseURL: 'http://localhost:3000',
  clientID: 'CLIENT_ID_FROM_DASHBOARD',
  issuerBaseURL: 'https://{your-tenant}.id.tuurio.com',
  // Logout (OIDC RP-initiated): end_session_endpoint + post_logout_redirect_uri=https://example.com/logout/success
};

// Add the auth middleware and you are done.
app.use(auth(config));

app.get('/', (req, res) => {
  res.send(req.oidc.isAuthenticated() ? 'Signed in as ' + req.oidc.user.name : 'Not signed in');
});

// Logout (OIDC RP-initiated)
app.get('/logout', async (req, res) => {
  const issuer = "https://{your-tenant}.id.tuurio.com";
  const discovery = await fetch(`${issuer}/.well-known/openid-configuration`).then(r => r.json());
  const endSession = discovery.end_session_endpoint;
  const returnTo = encodeURIComponent('https://example.com/logout/success');
  res.redirect(`${endSession}?post_logout_redirect_uri=${returnTo}`);
});
                        

Nota: il secret dovrebbe avere almeno 32 caratteri, altrimenti la libreria potrebbe rifiutare l'avvio.

Suggerimento: scopri automaticamente l'end_session_endpoint tramite /.well-known/openid-configuration.
app.py (Flask + Authlib) pip install Authlib Flask

from authlib.integrations.flask_client import OAuth
import requests

oauth = OAuth(app)
oauth.register(
    name='tuurio',
    client_id='CLIENT_ID_FROM_DASHBOARD',
    client_secret='CLIENT_SECRET',
    server_metadata_url='https://{tenant}.id.tuurio.com/.well-known/openid-configuration',
    # Logout (OIDC RP-initiated): end_session_endpoint + post_logout_redirect_uri=https://example.com/logout/success
    client_kwargs={'scope': 'openid profile email'}
)

@app.route('/login')
def login():
    redirect_uri = url_for('callback', _external=True)
    return oauth.tuurio.authorize_redirect(redirect_uri)

@app.route('/callback')
def callback():
    token = oauth.tuurio.authorize_access_token()
    user = token['userinfo']
    return f'Hello, {user["name"]}'

@app.route('/logout')
def logout():
    discovery = requests.get("https://{tenant}.id.tuurio.com/.well-known/openid-configuration").json()
    end_session = discovery["end_session_endpoint"]
    return redirect(f"{end_session}?post_logout_redirect_uri=https://example.com/logout/success")
                        
application.yml (Spring Boot Starter OAuth2 Client) Nessun codice manuale necessario!

spring:
  security:
    oauth2:
      client:
        registration:
          tuurio:
            client-id: CLIENT_ID_FROM_DASHBOARD
            client-secret: CLIENT_SECRET
            scope: [openid, profile, email]
            authorization-grant-type: authorization_code
            redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}"
            # Logout (OIDC RP-initiated): end_session_endpoint + post_logout_redirect_uri=https://example.com/logout/success
        provider:
          tuurio:
            issuer-uri: https://{your-tenant}.id.tuurio.com
                        

@RestController
class LogoutController {
  @GetMapping("/logout")
  void logout(HttpServletResponse response) throws IOException {
    String issuer = "https://{your-tenant}.id.tuurio.com";
    Map discovery = RestClient.create().get()
        .uri(issuer + "/.well-known/openid-configuration")
        .retrieve().body(Map.class);
    String endSession = (String) discovery.get("end_session_endpoint");
    String returnTo = URLEncoder.encode("https://example.com/logout/success", StandardCharsets.UTF_8);
    response.sendRedirect(endSession + "?post_logout_redirect_uri=" + returnTo);
  }
}
                        
Nota: Questo esempio mostra un client pubblico con PKCE. Per web app in produzione consigliamo un BFF (Backend for Frontend) cosi i token restano lato server. Dettagli nella guida alla sicurezza.
auth.ts (React SPA + oidc-client-ts, PKCE) npm install oidc-client-ts

// end_session_endpoint via discovery:
// const issuer = "https://{tenant}.id.tuurio.com";
// const discovery = await fetch(`${issuer}/.well-known/openid-configuration`).then(r => r.json());
// const endSessionEndpoint = discovery.end_session_endpoint;
                        

import { UserManager } from "oidc-client-ts";

const mgr = new UserManager({
  authority: "https://{tenant}.id.tuurio.com",
  client_id: "CLIENT_ID_FROM_DASHBOARD",
  redirect_uri: "http://localhost:5173/auth/callback",
  post_logout_redirect_uri: "http://localhost:5173/",
  response_type: "code",
  scope: "openid profile email",
  automaticSilentRenew: true
});

export const login = () => mgr.signinRedirect();
export const handleCallback = () => mgr.signinRedirectCallback();
// Logout uses end_session_endpoint from discovery
export const logout = () => mgr.signoutRedirect();
                        
Nota: Questo esempio mostra un client pubblico con PKCE. Per web app in produzione consigliamo un BFF (Backend for Frontend) cosi i token restano lato server. Dettagli nella guida alla sicurezza.
useAuth.ts (Vue 3 + oidc-client-ts) npm install oidc-client-ts

import { UserManager } from "oidc-client-ts";

const mgr = new UserManager({
  authority: "https://{tenant}.id.tuurio.com",
  client_id: "CLIENT_ID_FROM_DASHBOARD",
  redirect_uri: "http://localhost:5173/auth/callback",
  post_logout_redirect_uri: "http://localhost:5173/",
  response_type: "code",
  scope: "openid profile email"
});

export const useAuth = () => ({
  login: () => mgr.signinRedirect(),
  handleCallback: () => mgr.signinRedirectCallback(),
  // Logout uses end_session_endpoint from discovery
  logout: () => mgr.signoutRedirect()
});
                        
Nota: Questo esempio mostra un client pubblico con PKCE. Per web app in produzione consigliamo un BFF (Backend for Frontend) cosi i token restano lato server. Dettagli nella guida alla sicurezza.
auth.config.ts (Angular + angular-oauth2-oidc) npm install angular-oauth2-oidc

import { AuthConfig, OAuthService } from "angular-oauth2-oidc";

export const authConfig: AuthConfig = {
  issuer: "https://{tenant}.id.tuurio.com",
  clientId: "CLIENT_ID_FROM_DASHBOARD",
  redirectUri: window.location.origin + "/auth/callback",
  postLogoutRedirectUri: "http://localhost:5173/",
  responseType: "code",
  scope: "openid profile email"
};

export const initLogout = async (oauthService: OAuthService) => {
  const discovery = await fetch(`${authConfig.issuer}/.well-known/openid-configuration`).then(r => r.json());
  oauthService.logoutUrl = discovery.end_session_endpoint;
};

export const logout = (oauthService: OAuthService) => oauthService.logOut();
                        
Android (Kotlin) + AppAuth OAuth 2.0 + PKCE (Public Client)

val serviceConfig = AuthorizationServiceConfiguration(
    Uri.parse("https://{tenant}.id.tuurio.com/oauth2/authorize"),
    Uri.parse("https://{tenant}.id.tuurio.com/oauth2/token")
)

val request = AuthorizationRequest.Builder(
    serviceConfig,
    "CLIENT_ID_FROM_DASHBOARD",
    ResponseTypeValues.CODE,
    Uri.parse("com.example.app:/oauth2redirect")
)
    .setScope("openid profile email")
    .build()

// val postLogoutRedirectUri = Uri.parse("com.example.app:/logout")

val authService = AuthorizationService(context)
val intent = authService.getAuthorizationRequestIntent(request)
startActivityForResult(intent, RC_AUTH)

// Logout (OIDC RP-initiated): end_session_endpoint + post_logout_redirect_uri=com.example.app:/logout
AuthorizationServiceConfiguration.fetchFromUrl(
    Uri.parse("https://{tenant}.id.tuurio.com/.well-known/openid-configuration")
) { config, _ ->
    val endSession = EndSessionRequest.Builder(
        config!!,
        Uri.parse("com.example.app:/logout")
    ).build()
    val endSessionIntent = authService.getEndSessionRequestIntent(endSession)
    startActivityForResult(endSessionIntent, RC_LOGOUT)
}
                        

Suggerimento: AppAuth puo usare la discovery URL (/.well-known/openid-configuration) per evitare endpoint configurati a mano.

iOS (Swift) + AppAuth OAuth 2.0 + PKCE (Public Client)

let config = OIDServiceConfiguration(
  authorizationEndpoint: URL(string: "https://{tenant}.id.tuurio.com/oauth2/authorize")!,
  tokenEndpoint: URL(string: "https://{tenant}.id.tuurio.com/oauth2/token")!
)

let request = OIDAuthorizationRequest(
  configuration: config,
  clientId: "CLIENT_ID_FROM_DASHBOARD",
  scopes: [OIDScopeOpenID, OIDScopeProfile, OIDScopeEmail],
  redirectURL: URL(string: "com.example.app:/oauth2redirect")!,
  responseType: OIDResponseTypeCode,
  additionalParameters: nil
)

// let postLogoutRedirectURL = URL(string: "com.example.app:/logout")!

OIDAuthState.authState(byPresenting: request, presenting: self) { authState, error in
  // Store authState?.lastTokenResponse?.accessToken
}

// Logout (OIDC RP-initiated): end_session_endpoint + post_logout_redirect_uri=com.example.app:/logout
OIDAuthorizationService.discoverConfiguration(
  forIssuer: URL(string: "https://{tenant}.id.tuurio.com")!
) { config, _ in
  guard let config = config else { return }
  let endSession = OIDEndSessionRequest(
    configuration: config,
    idTokenHint: nil,
    postLogoutRedirectURL: URL(string: "com.example.app:/logout")!,
    additionalParameters: nil
  )
  self.present(OIDAuthorizationService.present(endSession, presenting: self) { _, _ in }, animated: true)
}
                        

Suggerimento: anche iOS AppAuth supporta discovery per caricare automaticamente la configurazione.

Flutter (Dart) + flutter_appauth flutter pub add flutter_appauth

import 'package:flutter_appauth/flutter_appauth.dart';

final appAuth = FlutterAppAuth();

final result = await appAuth.authorizeAndExchangeCode(
  AuthorizationTokenRequest(
    'CLIENT_ID_FROM_DASHBOARD',
    'com.example.app:/oauth2redirect',
    issuer: 'https://{tenant}.id.tuurio.com',
    scopes: ['openid', 'profile', 'email'],
  ),
);

// Logout (OIDC RP-initiated)
await appAuth.endSession(EndSessionRequest(
  idTokenHint: result?.idToken,
  postLogoutRedirectUrl: 'com.example.app:/logout',
  issuer: 'https://{tenant}.id.tuurio.com',
));
                        
main.go (golang.org/x/oauth2) go get golang.org/x/oauth2

var config = &oauth2.Config{
    ClientID:     "CLIENT_ID",
    ClientSecret: "CLIENT_SECRET",
    RedirectURL:  "http://localhost:3000/callback",
    Scopes:       []string{"openid", "profile", "email"},
    // Logout (OIDC RP-initiated): end_session_endpoint + post_logout_redirect_uri=https://example.com/logout/success
    Endpoint: oauth2.Endpoint{
        AuthURL:  "https://{tenant}.id.tuurio.com/oauth2/authorize",
        TokenURL: "https://{tenant}.id.tuurio.com/oauth2/token",
    },
}
// Nutze config.AuthCodeURL(...) und config.Exchange(...)

// Logout (OIDC RP-initiated)
resp, _ := http.Get("https://{tenant}.id.tuurio.com/.well-known/openid-configuration")
defer resp.Body.Close()
var discovery struct{ EndSessionEndpoint string `json:"end_session_endpoint"` }
json.NewDecoder(resp.Body).Decode(&discovery)
logoutUrl := discovery.EndSessionEndpoint + "?post_logout_redirect_uri=" + url.QueryEscape("https://example.com/logout/success")
                        
index.php (league/oauth2-client) composer require league/oauth2-client

$provider = new \League\OAuth2\Client\Provider\GenericProvider([
    'clientId'                => 'CLIENT_ID',
    'clientSecret'            => 'CLIENT_SECRET',
    'redirectUri'             => 'https://example.com/callback',
    'urlAuthorize'            => 'https://{tenant}.id.tuurio.com/oauth2/authorize',
    'urlAccessToken'          => 'https://{tenant}.id.tuurio.com/oauth2/token',
    'urlResourceOwnerDetails' => 'https://{tenant}.id.tuurio.com/userinfo',
    // Logout (OIDC RP-initiated): end_session_endpoint + post_logout_redirect_uri=https://example.com/logout/success
]);

// Logout (OIDC RP-initiated)
$discovery = json_decode(file_get_contents("https://{tenant}.id.tuurio.com/.well-known/openid-configuration"), true);
$endSession = $discovery['end_session_endpoint'];
header('Location: ' . $endSession . '?post_logout_redirect_uri=' . urlencode('https://example.com/logout/success'));
                        

Modella persone reali, non solo account

Molti prodotti possono iniziare con il login self-service. Se in seguito genitori, tutori, staff o membri responsabili devono agire per conto di qualcun altro, Tuurio puo modellarlo senza account fittizi.

Account e profilo non sono sempre la stessa cosa

Mantieni i flussi OIDC per le credenziali mentre archivi separatamente il profilo reale dove le operazioni lo richiedono.

Profili gestiti senza credenziali

Crea prima i profili di membri, studenti, persone a carico o volontari che non devono ancora ricevere il proprio login.

Continuita quando il login arriva piu tardi

Se un profilo gestito riceve piu avanti il proprio accesso, lo stesso profilo conserva relazioni e storia.

Controllo accessi integrato

Definisci i tuoi permessi (per esempio inventory:write o reports:view) direttamente nel dashboard Tuurio.

L'autorizzazione avviene nel token. Non nel tuo database.

Spring Security si aspetta authorities con prefisso SCOPE_ per default. Poiche Tuurio consegna i diritti nel claim permissions, usa un JwtAuthenticationConverter cosi @PreAuthorize("hasAuthority('inventory:write')") funziona senza prefisso.

I permessi arrivano nel claim permissions come array di stringhe.
JWT decodificato (Access Token)

{
  "sub": "user_12345",
  "iss": "https://dein-tenant.id.tuurio.com",
  "permissions": [
    "inventory:write",
    "reports:view"
  ],
  "roles": ["ADMIN"]
}
                                

Tuurio fornisce anche claim standard come email_verified o preferred_username, cosi non devi duplicarli nel database.

Esempio backend (Spring Security)

@PreAuthorize("hasAuthority('inventory:write')")
@PostMapping("/inventory")
public void updateStock() {
    // Tuurio hat's erlaubt!
}
                                    
SecurityConfig (Kotlin)

@Configuration
@EnableWebSecurity
@EnableMethodSecurity
class SecurityConfig {

    @Bean
    fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
        http
            .oauth2ResourceServer { oauth2 ->
                oauth2.jwt { jwt ->
                    jwt.jwtAuthenticationConverter(tuurioAuthenticationConverter())
                }
            }
        return http.build()
    }

    private fun tuurioAuthenticationConverter(): Converter {
        val converter = JwtAuthenticationConverter()
        converter.setJwtGrantedAuthoritiesConverter { jwt ->
            // Extrahiert das "permissions" Array aus dem Token
            val permissions = jwt.getClaim>("permissions") ?: emptyList()

            // Mapping zu SimpleGrantedAuthority
            permissions.map { SimpleGrantedAuthority(it) }
        }
        return converter
    }
}
                                    
Riferimento API

Documentazione di riferimento per gli endpoint di integrazione destinati ai client API usati dagli sviluppatori.

Apri il riferimento API ->
Guida alla sicurezza

Best practice per archiviazione dei token, PKCE e protezione CSRF.

Leggi la guida ->
Supporto

Bloccato nell'integrazione? Facciamo debug con te.

Contatta il supporto ->