Une connexion qui ne demande rien à personne.
Ajoutez « Continuer avec Droplet ID » à votre site ou votre app. Vos utilisateurs approuvent sur leur téléphone ; vous recevez un identifiant stable, propre à votre service. Pas de numéro, pas d’e-mail, pas de nom, pas de mot de passe à protéger.
- Votre site envoie la personne vers Droplet ID.
- Droplet ID affiche un code QR et un nombre à deux chiffres.
- Le téléphone scanne, la personne tape le nombre et approuve.
- Votre site reçoit un identifiant fait pour lui seul.
Démarrer en trois étapes
Enregistrez votre site
Écrivez à contact@dropletmesh.app avec le nom de votre service et vos adresses de retour exactes. Vous recevez un
client_id. Pendant la phase de test, l’enregistrement est manuel.Ajoutez le kit de votre langage
Un kit = une connexion vérifiée de bout en bout (PKCE, state, nonce, signature de l’ID token) et le bouton officiel.
Gardez le
subC’est l’identifiant de la personne pour votre service. Stable d’une connexion à l’autre, différent sur chaque autre site. Ne redemandez pas de numéro ni d’e-mail pour vous connecter : c’est la promesse faite à vos utilisateurs.
Les kits
Kits vérifié : essayés de bout en bout contre le service, à chaque modification. Guides : la bibliothèque OpenID Connect habituelle du langage, avec la bonne configuration.
vérifié @droplet/id · navigateur · aucune dépendance · source
import { DropletId, boutonDropletId } from '@droplet/id';
const did = new DropletId({
clientId: 'mon-site',
redirectUri: location.origin + '/',
portees: ['openid', 'profile'], // 'profile' : le pseudo, si la personne accepte
});
if (did.estUnRetour()) {
const { sub, pseudo } = await did.terminerConnexion(); // ID token vérifié
} else {
document.body.append(boutonDropletId({ onClick: () => did.connecter() }));
}
vérifié @droplet/id-react · utilisé par la démo Kola Café · source
import { DropletIdProvider, DropletIdButton, useDropletId } from '@droplet/id-react';
<DropletIdProvider clientId="mon-site" redirectUri={location.origin + '/'} portees={['openid', 'profile']}>
<App />
</DropletIdProvider>
function App() {
const { utilisateur, chargement, erreur, deconnecter } = useDropletId();
if (!utilisateur) return <DropletIdButton variante="sombre" />;
return <p>Bonjour {utilisateur.pseudo ?? 'vous'}</p>;
}
vérifié @droplet/id-node · par-dessus openid-client · utilisé par la démo Librairie Baobab · source
import express from 'express';
import { DropletIdServeur } from '@droplet/id-node';
const did = await DropletIdServeur.creer({
clientId: 'mon-site',
redirectUri: 'https://mon-site.cm/connexion/retour',
});
const app = express();
// GET /connexion et GET /connexion/retour, prêts :
app.use(did.routesExpress({
secret: process.env.SECRET, // 32 caractères ou plus
ouvrirSession: (req, res, { sub, pseudo }) => { /* votre session */ },
}));
vérifié droplet-id · une seule dépendance (cryptography) · Flask inclus · source
from droplet_id import DropletId
did = DropletId("mon-site", "https://mon-site.cm/connexion/retour")
url, attente = did.commencer() # gardez `attente` en session, puis redirigez vers url
session = did.terminer(url_de_retour, attente)
session["sub"]
# Avec Flask, en une ligne :
from droplet_id.flask import blueprint
app.register_blueprint(blueprint(client_id="mon-site", redirect_uri="https://mon-site.cm/connexion/retour"))
vérifié dropletmesh/droplet-id · sans dépendance, même sur un hébergement mutualisé · PHP 8.1+ · source
<?php
require 'DropletId.php';
session_start();
$did = new DropletMesh\DropletId('mon-site', 'https://mon-site.cm/connexion/retour');
// connexion.php
[$url, $_SESSION['droplet_id_attente']] = $did->commencer();
header("Location: $url");
// retour.php
$session = $did->terminer($urlComplete, $_SESSION['droplet_id_attente'] ?? null);
$_SESSION['utilisateur'] = $session['sub'];
vérifié par-dessus coreos/go-oidc et x/oauth2 · source
did, err := dropletid.Nouveau(ctx, dropletid.Options{
ClientID: "mon-site",
RedirectURI: "https://mon-site.cm/connexion/retour",
})
url, attente := did.Commencer() // gardez `attente` en session
session, err := did.Terminer(ctx, r.URL.Query(), attente)
session.Sub
compilé et testé droplet_id · par-dessus flutter_appauth · bouton officiel inclus · source
final did = DropletId(clientId: 'mon-app', redirectUri: 'cm.monapp:/connexion');
DropletIdBouton(onPressed: () async {
final session = await did.connecter(); // onglet sécurisé, jamais une WebView
print(session.sub);
});
// android/app/build.gradle :
// manifestPlaceholders += [appAuthRedirectScheme: 'cm.monapp']
compilé et testé app.dropletmesh.id · par-dessus AppAuth-Android · bouton Compose inclus · source
val did = DropletId(context, clientId = "mon-app", redirectUri = Uri.parse("cm.monapp:/connexion"))
val lanceur = rememberLauncherForActivityResult(StartActivityForResult()) { r ->
scope.launch { val session = did.terminer(r.data!!); /* session.sub */ }
}
DropletIdBouton(onClick = { scope.launch { lanceur.launch(did.intentionDeConnexion()) } })
guide Droplet ID suit OpenID Connect : la bibliothèque standard de votre langage suffit. Réglages communs : émetteur https://id.dropletmesh.app, flux authorization_code, PKCE S256, portées openid (+ profile), client public sans secret.
Java — Spring Security (application.yml)
spring.security.oauth2.client:
registration.droplet:
client-id: mon-site
client-authentication-method: none
authorization-grant-type: authorization_code
redirect-uri: "{baseUrl}/login/oauth2/code/droplet"
scope: openid
provider.droplet:
issuer-uri: https://id.dropletmesh.app
C# — ASP.NET Core
builder.Services.AddAuthentication(o => { o.DefaultScheme = "Cookies"; o.DefaultChallengeScheme = "droplet"; })
.AddCookie("Cookies")
.AddOpenIdConnect("droplet", o => {
o.Authority = "https://id.dropletmesh.app";
o.ClientId = "mon-site";
o.ResponseType = "code";
o.UsePkce = true;
o.Scope.Clear(); o.Scope.Add("openid");
});
Ruby — OmniAuth
use OmniAuth::Builder do
provider :openid_connect,
issuer: "https://id.dropletmesh.app", discovery: true,
pkce: true, scope: [:openid], response_type: :code,
client_options: { identifier: "mon-site", redirect_uri: "https://mon-site.cm/auth/openid_connect/callback" }
end
Swift — AppAuth-iOS
OIDAuthorizationService.discoverConfiguration(forIssuer: URL(string: "https://id.dropletmesh.app")!) { conf, _ in
let req = OIDAuthorizationRequest(configuration: conf!, clientId: "mon-app", scopes: [OIDScopeOpenID],
redirectURL: URL(string: "cm.monapp:/connexion")!, responseType: OIDResponseTypeCode,
additionalParameters: nil)
session = OIDAuthState.authState(byPresenting: req, presenting: self) { etat, _ in /* etat?.lastTokenResponse?.idToken */ }
}
Ces quatre guides utilisent les bibliothèques officielles de chaque langage ; ils n’ont pas encore d’essai automatique contre le service.
Bac à sable
Connectez-vous ici, dans l’environnement de test, et voyez exactement ce que reçoit un site. Pas de téléphone sous la main ? La page de connexion propose un appareil de test qui joue son rôle dans un autre onglet.
Le bac à sable utilise le kit JavaScript, comme le ferait votre site.
Rien reçu pour l’instant.
Deux sites de démonstration
Référence
| Émetteur | https://id.dropletmesh.app · test : https://id-test.dropletmesh.app |
|---|---|
| Découverte | /.well-known/openid-configuration |
| Flux | authorization_code + PKCE S256 obligatoire · refresh_token à usage unique |
| Portées | openid · profile (pseudo choisi par site) · offline_access. Il n’existe ni email ni phone. |
| Identifiant | sub « pairwise » : un par site, stable |
| Signature | ES256, clés sur /.well-known/jwks.json |
| Durées | ID token 5 min · access token 10 min · refresh token 30 jours · code 60 s |
| Rester connecté | Session Droplet ID : 14 jours dans le navigateur (cookie persistant). Session du site avec le kit JS : 30 jours (dureeSession), conservée après fermeture du navigateur. |
| Déconnexion | /oauth2/sessions/logout (OpenID RP-Initiated Logout) |