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.