Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
179 changes: 179 additions & 0 deletions .github/fixtures/workflow-compat.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
# Smoke fixture for workflow-compat CI: exercises every step type this
# plugin exposes, using the BMW-supplied config field shapes that v0.2.2
# closed. Validated by `wfctl validate` against the LATEST wfctl release
# in .github/workflows/workflow-compat.yml.

modules:
- name: auth-credential
type: auth.credential
config:
rp_display_name: "Compat Smoke"
rp_id: "example.test"
origin: "https://example.test"
optional: true

workflows:
smoke:
trigger:
type: http
config:
path: /smoke
method: POST
steps:
- name: methods_policy
type: step.auth_methods_policy
config:
environment: "development"
password_auth_enabled: true
totp_auth_enabled: false
webauthn_rp_id: "example.test"
webauthn_origin: "https://example.test"
smtp_host: "smtp.example.test"
smtp_from: "noreply@example.test"
auth_routes_enabled: true
routes_enabled: true
oauth_routes_enabled: true
google_oauth_client_id: "google-client"
google_oauth_client_secret: "google-secret"
google_oauth_redirect_url: "https://example.test/cb"
# Fields added in v0.2.2 — strict-proto rejection gaps closed:
jwt_secret: "test-jwt-secret"
sms_auth_enabled: false
facebook_oauth_client_id: "fb-client"
facebook_oauth_client_secret: "fb-secret"
instagram_oauth_client_id: "ig-client"
instagram_oauth_client_secret: "ig-secret"
x_oauth_client_id: "x-client"
x_oauth_client_secret: "x-secret"
twilio_verify_service_sid: "VA000"
twilio_account_sid: "AC000"
twilio_auth_token: "twilio-token"
twilio_api_key_sid: "SK000"
twilio_api_key_secret: "twilio-key-secret"
password_hash_count: 0

- name: policy_gate
type: step.auth_policy_gate
config:
policy_step: methods_policy
signing_secret: "test-secret"
jwt_secret: "test-jwt-secret"
required_runtime_keys:
- tenant_id
oauth_supported_providers:
- google
# Added in v0.2.2:
tenant_id: "tenant-smoke"

- name: methods_response
type: step.auth_methods_response

- name: policy_audit
type: step.auth_policy_audit
config:
environment: "development"
password_auth_enabled: true
jwt_secret: "test-jwt-secret"

- name: passkey_begin_register
type: step.auth_passkey_begin_register
config:
module: auth-credential

- name: passkey_finish_register
type: step.auth_passkey_finish_register
config:
module: auth-credential

- name: passkey_begin_login
type: step.auth_passkey_begin_login
config:
module: auth-credential

- name: passkey_finish_login
type: step.auth_passkey_finish_login
config:
module: auth-credential

- name: totp_generate_secret
type: step.auth_totp_generate_secret

- name: totp_verify
type: step.auth_totp_verify

- name: totp_recovery_codes
type: step.auth_totp_recovery_codes

- name: magic_link_generate
type: step.auth_magic_link_generate

- name: magic_link_verify
type: step.auth_magic_link_verify

- name: magic_link_send
type: step.auth_magic_link_send
config:
smtp_host: "smtp.example.test"
smtp_port: 587
smtp_user: "smtp-user"
smtp_pass: "smtp-pass"
from_address: "noreply@example.test"

- name: password_hash
type: step.auth_password_hash

- name: password_verify
type: step.auth_password_verify

- name: challenge_generate
type: step.auth_challenge_generate

- name: challenge_verify
type: step.auth_challenge_verify
config:
# Field added in v0.2.2 (replaced EmptyConfig):
signing_secret: "test-secret"

- name: normalize_phone
type: step.auth_normalize_phone

- name: oauth_provider_config
type: step.auth_oauth_provider_config
config:
provider: google
google_oauth_client_id: "google-client"
google_oauth_client_secret: "google-secret"
google_oauth_redirect_url: "https://example.test/cb"

- name: oauth_start
type: step.auth_oauth_start
config:
provider: google
google_oauth_client_id: "google-client"
google_oauth_client_secret: "google-secret"
google_oauth_redirect_url: "https://example.test/cb"

- name: oauth_exchange
type: step.auth_oauth_exchange
config:
provider: google
google_oauth_client_id: "google-client"
google_oauth_client_secret: "google-secret"
google_oauth_redirect_url: "https://example.test/cb"
# Note: BMW currently passes `code` here — that is a BMW yaml bug.
# `code` belongs in OAuthProviderInput (i.e. via a preceding
# step.set or trigger.query). Not present in this fixture.

- name: oauth_userinfo
type: step.auth_oauth_userinfo
config:
provider: google
google_oauth_client_id: "google-client"
google_oauth_client_secret: "google-secret"
google_oauth_redirect_url: "https://example.test/cb"

- name: credential_list
type: step.auth_credential_list

- name: credential_revoke
type: step.auth_credential_revoke
73 changes: 73 additions & 0 deletions .github/workflows/workflow-compat.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
name: Workflow Compat (latest wfctl)

# Continuously verify this plugin's strict-proto contracts stay compatible
# with the LATEST published wfctl release — independently of the SDK
# version pinned in go.mod. Surfaces drift the same day a workflow release
# introduces a stricter validator.

on:
push:
branches: [main]
pull_request:
branches: [main]
schedule:
# Daily at 09:00 UTC so any post-merge workflow release surfaces drift.
- cron: "0 9 * * *"

permissions:
contents: read

env:
GOPRIVATE: github.com/GoCodeAlone/*

jobs:
validate-against-latest:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: go.mod

- name: Configure Git for private repos
env:
RELEASES_TOKEN: ${{ secrets.RELEASES_TOKEN }}
run: |
if [ -n "$RELEASES_TOKEN" ]; then
git config --global url."https://x-access-token:${RELEASES_TOKEN}@github.com/".insteadOf "https://github.com/"
fi

- name: Resolve latest wfctl release tag
id: wfctl
run: |
tag=$(curl -fsSL https://api.github.com/repos/GoCodeAlone/workflow/releases/latest | jq -r .tag_name)
echo "tag=$tag" >> "$GITHUB_OUTPUT"
echo "Latest wfctl tag: $tag"

- uses: GoCodeAlone/setup-wfctl@bcd880980f5bbe8d192d0c20ff6279d25331f956
with:
version: ${{ steps.wfctl.outputs.tag }}

- name: Validate plugin manifest strict-contracts
run: wfctl plugin validate --file plugin.json --strict-contracts

- name: Build plugin binary
run: |
mkdir -p bin
go build -o bin/workflow-plugin-auth ./cmd/workflow-plugin-auth

- name: Stage plugin for wfctl plugin-dir discovery
run: |
mkdir -p /tmp/wfctl-plugins/workflow-plugin-auth
cp bin/workflow-plugin-auth plugin.json plugin.contracts.json /tmp/wfctl-plugins/workflow-plugin-auth/

- name: Validate fixture pipeline (latest wfctl + plugin discovery)
run: |
# Smoke fixture exercises every plugin step type. plugin-dir
# makes wfctl load this plugin's contracts, so any drift between
# the BMW-supplied config shapes and the plugin's strict-proto
# descriptors will surface as a config-validation failure.
wfctl validate \
--plugin-dir /tmp/wfctl-plugins \
-allow-no-entry-points \
.github/fixtures/workflow-compat.yaml
33 changes: 33 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Changelog

## v0.2.2 (2026-05-13)

### Strict-proto config-field gaps closed (BMW local smoke vs workflow v0.51.5)

- `AuthMethodsPolicyConfig`/`AuthMethodsPolicyInput` (used by `step.auth_methods_policy`
and `step.auth_policy_audit`): added BMW-supplied fields that the typed proto
was rejecting under strict-contracts:
- `jwt_secret` (string, tag 24)
- `sms_auth_enabled` (optional bool, tag 25) — alongside existing `sms_enabled`
- `facebook_oauth_client_id`, `facebook_oauth_client_secret` (tags 26-27)
- `instagram_oauth_client_id`, `instagram_oauth_client_secret` (tags 28-29)
- `x_oauth_client_id`, `x_oauth_client_secret` (tags 30-31)
- `AuthPolicyGateConfig` (used by `step.auth_policy_gate`): added
- `tenant_id` (string, tag 6) — BMW supplies this directly in the gate config block
- `step.auth_challenge_verify`: replaced `EmptyConfig` with new
`AuthChallengeVerifyConfig { string signing_secret = 1; }`. The handler now
falls back to `config.signing_secret` when input does not carry one.

### Known deferred (BMW yaml bug — does not require plugin change)

- `step.auth_oauth_exchange`: BMW currently passes `code` inside the step's
`config:` block. The handler reads `code` from `req.Input` (or merged
`current`) — `code` belongs in `OAuthProviderInput`, not `OAuthProviderConfig`.
BMW must move it to a runtime input (e.g. `current.code`, supplied via a
preceding `step.set` from `parse_request.query.code`). No plugin change made.

### CI

- New `.github/workflows/workflow-compat.yml` runs on every PR:
builds the plugin, installs latest `wfctl` release, and validates a
minimal smoke pipeline that exercises every plugin step type.
Loading
Loading