Airlock

 view release on metacpan or  search on metacpan

docs/superpowers/plans/2026-10-02-airlock-phase-1.md  view on Meta::CPAN

  };
}

=method subject

    my $subject = $keycloak->subject($claims);

The Airlock subject for a set of token claims: C<id> from C<sub>, and C<amr>,
C<acr> and C<auth_time> as Keycloak sent them. Croaks without C<sub>.

=cut

sub factor {
  my ( $self, %arg ) = @_;
  return $self->factor_class->new( accept_amr => $self->mfa_amr, accept_acr => $self->mfa_acr, %arg );
}

=method factor

    my $factor = $keycloak->factor( max_age => 300 );

An L<Airlock::Factor::Upstream> that holds for a Keycloak login with a second
factor. Takes its options.

=cut

1;
```

- [ ] **Step 4: Unit-Test laufen lassen, Modul in den Load-Test**

Run: `prove -lr t/45-upstream-keycloak.t` — Expected: PASS (2 Subtests). Dann `  Airlock::Upstream::Keycloak` in die Liste in `t/00-load.t` und `prove -lr t` — Expected: PASS.

- [ ] **Step 5: Realm, Anleitung und Live-Test schreiben**

`t/keycloak/realm.json`:

```json
{
  "realm": "airlock-test",
  "enabled": true,
  "clients": [
    {
      "clientId": "airlock-test-cli",
      "publicClient": true,
      "standardFlowEnabled": false,
      "directAccessGrantsEnabled": true,
      "attributes": {
        "oauth2.device.authorization.grant.enabled": "true"
      }
    }
  ],
  "users": [
    {
      "username": "plain",
      "enabled": true,
      "email": "plain@example.org",
      "emailVerified": true,
      "firstName": "Plain",
      "lastName": "User",
      "credentials": [
        { "type": "password", "value": "plain-password", "temporary": false }
      ]
    },
    {
      "username": "otp",
      "enabled": true,
      "email": "otp@example.org",
      "emailVerified": true,
      "firstName": "Otp",
      "lastName": "User",
      "credentials": [
        { "type": "password", "value": "otp-password", "temporary": false },
        {
          "type": "otp",
          "secretData": "{\"value\":\"12345678901234567890\"}",
          "credentialData": "{\"subType\":\"totp\",\"digits\":6,\"counter\":0,\"period\":30,\"algorithm\":\"HmacSHA1\"}"
        }
      ]
    }
  ]
}
```

`t/keycloak/README.md`:

````markdown
# Keycloak live test

`t/90-live-keycloak.t` runs only when `TEST_AIRLOCK_KEYCLOAK_URL` is set. It
needs a Keycloak with the realm `airlock-test` from `realm.json` in this
directory: a public client `airlock-test-cli` with the device authorization
grant and direct access grants enabled, a user `plain` with a password, and a
user `otp` with a password and a TOTP credential whose secret is the RFC 6238
test secret `12345678901234567890`.

## Start a throwaway Keycloak

```bash
docker run --rm --name airlock-keycloak -p 8080:8080 \
  -e KC_BOOTSTRAP_ADMIN_USERNAME=admin -e KC_BOOTSTRAP_ADMIN_PASSWORD=admin \
  -v "$PWD/t/keycloak/realm.json:/opt/keycloak/data/import/realm.json:ro" \
  quay.io/keycloak/keycloak:latest start-dev --import-realm
```

Keycloak needs about 1 GB of memory. Do not start it on a machine that is
already short of it.

## Run

```bash
TEST_AIRLOCK_KEYCLOAK_URL=http://localhost:8080 prove -lv t/90-live-keycloak.t
```

## Findings

Recorded by whoever ran the test last. The `diag` lines of the test print
what to copy here.

| Keycloak version | Login | acr | amr | auth_time |
|---|---|---|---|---|
````

`t/90-live-keycloak.t`:

```perl
#!/usr/bin/env perl
use strict;
use warnings;
use Test::More;

# Live test against a real Keycloak. Off unless TEST_AIRLOCK_KEYCLOAK_URL is



( run in 0.516 second using v1.01-cache-2.11-cpan-3f962061493 )