Code samples
Sign-in with Blind.ID in four common stacks. Each is the library's ordinary OpenID Connect support pointed at https://sandbox.blind.id; nothing here is specific to us beyond the address. Register the site in the developer console first, and see Sign-in for what comes back.
ASP.NET Core Next.js (Auth.js) Python (Flask and Authlib) PHP
ASP.NET Core
dotnet add package Microsoft.AspNetCore.Authentication.OpenIdConnect
Program.cs
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthentication(o =>
{
o.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
o.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(o =>
{
o.Authority = "https://sandbox.blind.id";
o.ClientId = builder.Configuration["BlindId:ClientId"];
o.ClientSecret = builder.Configuration["BlindId:ClientSecret"];
o.ResponseType = "code";
o.UsePkce = true;
o.Scope.Clear();
o.Scope.Add("openid");
o.SaveTokens = true; // keeps the ID token for sign-out
o.MapInboundClaims = false; // keep "sub" as "sub"
o.GetClaimsFromUserInfoEndpoint = true;
o.SignedOutCallbackPath = "/signout-callback-oidc";
});
builder.Services.AddAuthorization();
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapGet("/", (HttpContext http) => http.User.Identity?.IsAuthenticated == true
? Results.Text($"Signed in as {http.User.FindFirst("sub")?.Value}")
: Results.Text("Not signed in. Go to /login"));
app.MapGet("/login", () => Results.Challenge(
new AuthenticationProperties { RedirectUri = "/" },
[OpenIdConnectDefaults.AuthenticationScheme]));
app.MapGet("/logout", () => Results.SignOut(
new AuthenticationProperties { RedirectUri = "/" },
[CookieAuthenticationDefaults.AuthenticationScheme, OpenIdConnectDefaults.AuthenticationScheme]));
app.Run();
Register https://your-site/signin-oidc as the redirect address and https://your-site/signout-callback-oidc as the sign-out return address. Keep the secret in user secrets or your host's configuration, not in appsettings.json.
Next.js (Auth.js)
npm install next-auth@beta
auth.ts
import NextAuth from "next-auth";
export const { handlers, auth, signIn, signOut } = NextAuth({
providers: [
{
id: "blindid",
name: "Blind.ID",
type: "oidc",
issuer: "https://sandbox.blind.id/",
clientId: process.env.AUTH_BLINDID_ID,
clientSecret: process.env.AUTH_BLINDID_SECRET,
checks: ["pkce", "state", "nonce"],
authorization: { params: { scope: "openid" } },
},
],
});
app/api/auth/[...nextauth]/route.ts
import { handlers } from "@/auth";
export const { GET, POST } = handlers;
app/page.tsx
import { auth, signIn, signOut } from "@/auth";
export default async function Home() {
const session = await auth();
if (!session) {
return (
<form action={async () => { "use server"; await signIn("blindid"); }}>
<button>Sign in with Blind.ID</button>
</form>
);
}
return (
<form action={async () => { "use server"; await signOut(); }}>
<p>Signed in.</p>
<button>Sign out</button>
</form>
);
}
Register https://your-site/api/auth/callback/blindid as the redirect address. Set AUTH_SECRET as Auth.js requires. The person's identifier is the token's sub; read it in the jwt callback if your app stores users.
Python (Flask and Authlib)
pip install flask authlib requests
app.py
import os
from flask import Flask, redirect, session, url_for
from authlib.integrations.flask_client import OAuth
app = Flask(__name__)
app.secret_key = os.environ["FLASK_SECRET_KEY"]
oauth = OAuth(app)
oauth.register(
name="blindid",
server_metadata_url="https://sandbox.blind.id/.well-known/openid-configuration",
client_id=os.environ["BLINDID_CLIENT_ID"],
client_secret=os.environ["BLINDID_CLIENT_SECRET"],
client_kwargs={"scope": "openid", "code_challenge_method": "S256"},
)
@app.route("/")
def home():
user = session.get("user")
return f"Signed in as {user['sub']}" if user else '<a href="/login">Sign in with Blind.ID</a>'
@app.route("/login")
def login():
return oauth.blindid.authorize_redirect(url_for("callback", _external=True))
@app.route("/callback")
def callback():
token = oauth.blindid.authorize_access_token()
session["user"] = token["userinfo"] # verified ID token claims
session["id_token"] = token["id_token"]
return redirect("/")
@app.route("/logout")
def logout():
id_token = session.pop("id_token", None)
session.clear()
metadata = oauth.blindid.load_server_metadata()
return redirect(metadata["end_session_endpoint"]
+ "?id_token_hint=" + (id_token or "")
+ "&post_logout_redirect_uri=" + url_for("home", _external=True))
Register https://your-site/callback as the redirect address and https://your-site/ as the sign-out return address.
PHP
composer require jumbojett/openid-connect-php
login.php
<?php
require __DIR__ . '/vendor/autoload.php';
use Jumbojett\OpenIDConnectClient;
session_start();
$oidc = new OpenIDConnectClient(
'https://sandbox.blind.id',
getenv('BLINDID_CLIENT_ID'),
getenv('BLINDID_CLIENT_SECRET')
);
$oidc->setRedirectURL('https://your-site/login.php');
$oidc->setCodeChallengeMethod('S256');
$oidc->addScope(['openid']);
// Sends the browser to Blind.ID, and on the way back redeems the code.
$oidc->authenticate();
$_SESSION['sub'] = $oidc->requestUserInfo('sub');
$_SESSION['id_token'] = $oidc->getIdToken();
header('Location: /');
logout.php
<?php
require __DIR__ . '/vendor/autoload.php';
use Jumbojett\OpenIDConnectClient;
session_start();
$idToken = $_SESSION['id_token'] ?? null;
session_destroy();
$oidc = new OpenIDConnectClient('https://sandbox.blind.id', getenv('BLINDID_CLIENT_ID'), getenv('BLINDID_CLIENT_SECRET'));
$oidc->signOut($idToken, 'https://your-site/');
Register https://your-site/login.php as the redirect address and https://your-site/ as the sign-out return address. For WordPress, use the plugin instead.