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@1.7.0
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.

Model Context Protocol

Offri agli assistenti IA un accesso controllato a Tuurio

MCP Streamable HTTP usa OAuth nel browser, PKCE, token vincolati alle risorse e i permessi effettivi dell'utente connesso.

  • Usa /mcp per le normali operazioni del tenant.
  • Usa /mcp/setup solo per l'onboarding guidato.
  • Richiedi mcp:write esplicitamente per modifiche approvate.
Guida MCP completa
CodexOAuth + PKCE
codex mcp add tuurio-auth --url https://<tenant>.id.tuurio.com/mcp
codex mcp login tuurio-auth --scopes mcp:connect,mcp:write

Per la sola lettura, ometti mcp:write. Gli host con registrazione dinamica compatibile non richiedono un client creato in anticipo.

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 requires the ID token from the validated login session.
};

// 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 idTokenHint = req.oidc.idToken;
  if (!idTokenHint) return res.redirect('/');
  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 params = new URLSearchParams({
    client_id: 'CLIENT_ID_FROM_DASHBOARD',
    id_token_hint: idTokenHint,
    post_logout_redirect_uri: 'https://example.com/logout/success'
  });
  res.redirect(`${endSession}?${params}`);
});
                        

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 Flask-Session redis requests

import os
from authlib.integrations.flask_client import OAuth
from flask_session import Session
from redis import Redis
import requests
from urllib.parse import urlencode

# Flask's default session is a signed browser cookie. Use a server-side store
# before retaining the validated ID token for RP-initiated logout.
app.config.update(
    SESSION_TYPE="redis",
    SESSION_REDIS=Redis.from_url(os.environ["REDIS_URL"]),
    SESSION_COOKIE_SECURE=True,
    SESSION_COOKIE_HTTPONLY=True,
    SESSION_COOKIE_SAMESITE="Lax",
)
Session(app)

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',
    # Keep the validated ID token in the encrypted server session for logout.
    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()
    session["tuurio_id_token"] = token["id_token"]
    user = token['userinfo']
    return f'Hello, {user["name"]}'

@app.route('/logout')
def logout():
    id_token_hint = session.pop("tuurio_id_token", None)
    if not id_token_hint:
        return redirect(url_for("index"))
    discovery = requests.get("https://{tenant}.id.tuurio.com/.well-known/openid-configuration").json()
    end_session = discovery["end_session_endpoint"]
    params = urlencode({
        "client_id": "CLIENT_ID_FROM_DASHBOARD",
        "id_token_hint": id_token_hint,
        "post_logout_redirect_uri": "https://example.com/logout/success",
    })
    return redirect(f"{end_session}?{params}")
                        
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}"
            # Spring's OIDC logout handler supplies the validated ID token hint.
        provider:
          tuurio:
            issuer-uri: https://{your-tenant}.id.tuurio.com
                        

@Bean
OidcClientInitiatedLogoutSuccessHandler oidcLogoutSuccessHandler(
    ClientRegistrationRepository registrations) {
  var handler = new OidcClientInitiatedLogoutSuccessHandler(registrations);
  handler.setPostLogoutRedirectUri("https://example.com/logout/success");
  return handler;
}

@Bean
SecurityFilterChain securityFilterChain(
    HttpSecurity http,
    OidcClientInitiatedLogoutSuccessHandler oidcLogoutSuccessHandler) throws Exception {
  http
      .authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
      .oauth2Login(Customizer.withDefaults())
      .logout(logout -> logout.logoutSuccessHandler(oidcLogoutSuccessHandler));
  return http.build();
}
                        
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 uses the validated ID token retained in AuthState.
AuthorizationServiceConfiguration.fetchFromUrl(
    Uri.parse("https://{tenant}.id.tuurio.com/.well-known/openid-configuration")
) { config, _ ->
    val idTokenHint = authState.lastTokenResponse?.idToken ?: return@fetchFromUrl
    val endSession = EndSessionRequest.Builder(config!!)
        .setIdTokenHint(idTokenHint)
        .setPostLogoutRedirectUri(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 uses the validated ID token retained in AuthState.
OIDAuthorizationService.discoverConfiguration(
  forIssuer: URL(string: "https://{tenant}.id.tuurio.com")!
) { config, _ in
  guard let config = config else { return }
  let endSession = OIDEndSessionRequest(
    configuration: config,
    idTokenHint: authState?.lastTokenResponse?.idToken,
    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"},
    // Retain the validated ID token in the server-side session for logout.
    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). idTokenHint comes from validated server session state.
func logoutURL(idTokenHint string) string {
    if idTokenHint == "" { return "/" }
    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)
    values := url.Values{
        "client_id": {"CLIENT_ID_FROM_DASHBOARD"},
        "id_token_hint": {idTokenHint},
        "post_logout_redirect_uri": {"https://example.com/logout/success"},
    }
    return discovery.EndSessionEndpoint + "?" + values.Encode()
}
                        
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',
    // Retain the validated ID token in the server-side PHP session for logout.
]);

// 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'];
$idTokenHint = $_SESSION['tuurio_id_token'] ?? '';
unset($_SESSION['tuurio_id_token']);
if ($idTokenHint === '') {
    header('Location: /');
    exit;
}
$params = http_build_query([
    'client_id' => 'CLIENT_ID_FROM_DASHBOARD',
    'id_token_hint' => $idTokenHint,
    'post_logout_redirect_uri' => 'https://example.com/logout/success',
]);
header('Location: ' . $endSession . '?' . $params);
                        

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 ->