Skip to content

pixlcore/xyplug-sso-aws-alb

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

xyOps AWS ALB SSO Plugin

xyplug-sso-aws-alb is a custom SSO command plugin for xyOps.

It is designed for AWS Application Load Balancers that use native OIDC authentication at the listener layer. The plugin reads the ALB headers that AWS forwards to your backend, validates the ALB-signed JWT, extracts user claims, and emits standard trusted headers back to xyOps.

Please read the xyOps SSO documentation before proceeding.

What it does

The plugin reads these incoming AWS ALB headers from the xyOps SSO command request:

  • x-amzn-oidc-identity
  • x-amzn-oidc-data

It then:

  1. Verifies the JWT from x-amzn-oidc-data using aws-jwt-verify.
  2. Validates the ALB signer ARN, issuer, and client ID.
  3. Extracts identity and claims from the verified payload.
  4. Emits headers that match your xyOps header_map, such as:
    • x-forwarded-user
    • x-forwarded-name
    • x-forwarded-email
    • x-forwarded-groups

xyOps then continues through its normal SSO flow as if those trusted headers had been forwarded by a traditional auth proxy.

Requirements

  • Node.js 18 or higher
  • npx available on the xyOps conductor
  • xyOps with SSO custom command support (v1.0.40+)
  • An AWS ALB listener already configured for OIDC authentication
  • Network egress from the xyOps conductor to the AWS ALB public key endpoint on the first verification of each new token or key ID

Install and run

The plugin is designed to be invoked by xyOps itself through sso.json.

Recommended command:

"command": "npx -y @pixlcore/xyplug-sso-aws-alb@1.0.0"

If you prefer, you can also preinstall the package and execute it directly:

"command": "xyplug-sso-aws-alb"

That works if the executable is already on your server PATH.

How xyOps calls the plugin

xyOps launches the command before its normal SSO header processing. It sends a one-line JSON document to STDIN containing:

  • the full SSO config object
  • the request headers
  • parsed cookies
  • request IP metadata

The plugin must return a one-line JSON document on STDOUT using the xyOps wire protocol:

{
	"xy": 1,
	"code": 0,
	"headers": {
		"x-forwarded-user": "user@example.com",
		"x-forwarded-name": "Example User",
		"x-forwarded-email": "user@example.com",
		"x-forwarded-groups": "admins,operators"
	}
}

Important:

  • Logical auth failures are returned with code: 1 in JSON.
  • The plugin is intentionally written to emit JSON and exit normally, so xyOps can display a meaningful SSO error.

Recommended xyOps configuration

Edit /opt/xyops/conf/sso.json and configure SSO like this:

{
	"enabled": true,
	"whitelist": ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"],
	"header_map": {
		"username": "x-forwarded-user",
		"full_name": "x-forwarded-name",
		"email": "x-forwarded-email",
		"groups": "x-forwarded-groups"
	},
	"cleanup_username": false,
	"cleanup_full_name": false,
	"group_role_map": {},
	"group_privilege_map": {},
	"command": "npx -y @pixlcore/xyplug-sso-aws-alb@1.0.0",
	"jwt": {
		"provider": "aws_alb",
		"header": "x-amzn-oidc-data",
		"claim_map": {
			"username": "email",
			"email": "email",
			"full_name": "name",
			"groups": "groups"
		},
		"aws_alb": {
			"alb_arn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:loadbalancer/app/example/abc123",
			"issuer": "https://accounts.google.com",
			"client_id": "your-oidc-client-id",
			"grace_seconds": 0
		}
	}
}

This works because:

  • xyOps only cares about the standard trusted headers in header_map
  • xyOps ignores the custom jwt block
  • the plugin reads jwt from the command input, verifies the AWS token, and prints matching headers back out

Header map recommendations

Recommended header map:

"header_map": {
	"username": "x-forwarded-user",
	"full_name": "x-forwarded-name",
	"email": "x-forwarded-email",
	"groups": "x-forwarded-groups"
}

Using distinct headers is the cleanest configuration.

You can map multiple xyOps fields to the same header name if you want, for example:

"header_map": {
	"username": "x-forwarded-user",
	"full_name": "x-forwarded-user",
	"email": "x-forwarded-email",
	"groups": "x-forwarded-groups"
}

In that case, the plugin emits only one value for the shared header name. This is useful when you want xyOps to derive a prettier full name using cleanup_full_name.

Recommendation:

  • Use distinct headers if you want the plugin to pass a real full name.
  • Reuse the username header only if you intentionally want xyOps to derive the display name itself.

AWS JWT configuration

The plugin expects a jwt object in sso.json.

jwt.provider

Set this to:

"provider": "aws_alb"

jwt.header

This is the incoming request header that contains the ALB JWT.

For AWS ALB, use:

"header": "x-amzn-oidc-data"

jwt.claim_map

This tells the plugin which verified claims to use for xyOps fields.

Example:

"claim_map": {
	"email": "email",
	"full_name": "name",
	"groups": "groups"
}

Each value may be:

  • a string claim name such as "email"
  • a dotted path such as "custom.profile.name"
  • an array of fallbacks such as ["name", "preferred_username", "email"]

Common mappings:

  • username: often email or preferred_username
  • email: usually email
  • full_name: usually name
  • groups: depends on your IdP, often groups, roles, or a custom claim

jwt.aws_alb.alb_arn

This must match the AWS ALB signer ARN.

Example:

"alb_arn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:loadbalancer/app/example/abc123"

You may also provide an array if you trust multiple ALBs for the same issuer:

"alb_arn": [
	"arn:aws:elasticloadbalancing:us-west-2:123456789012:loadbalancer/app/alb-a/abc123",
	"arn:aws:elasticloadbalancing:us-west-2:123456789012:loadbalancer/app/alb-b/def456"
]

jwt.aws_alb.issuer

This must match the JWT iss claim from your OIDC provider.

Examples:

  • https://accounts.google.com
  • https://YOUR_OKTA_DOMAIN/oauth2/default
  • https://cognito-idp.us-west-2.amazonaws.com/us-west-2_ABC123

jwt.aws_alb.client_id

This must match the ALB JWT client identifier.

Example:

"client_id": "your-oidc-client-id"

You may also set it to null if you intentionally want to skip client validation, but that is not recommended.

jwt.aws_alb.grace_seconds

Optional clock-skew allowance in seconds.

Example:

"grace_seconds": 30

Optional advanced settings

The plugin also understands these optional properties:

"jwt": {
	"cache_dir": "/tmp/xyplug-sso-aws-alb-cache",
	"aws_alb": {
		"jwks_uri": "https://public-keys.auth.elb.us-west-2.amazonaws.com"
	}
}

Notes:

  • cache_dir overrides the default on-disk token cache path.
  • jwks_uri is mainly useful for advanced testing. In normal AWS setups you should omit it and let aws-jwt-verify derive the correct ALB public key endpoint from the ALB ARN region.

Username, email, and full name behavior

The plugin resolves values in this general order:

Username

Preferred sources:

  1. claim_map.username
  2. common fallback claims such as preferred_username, username, email
  3. x-amzn-oidc-identity
  4. JWT sub

Note:

  • AWS ALB sets x-amzn-oidc-identity to the OIDC sub claim.
  • In many environments that is a stable opaque identifier rather than a friendly username.
  • For that reason, the plugin prefers mapped claims first and uses the ALB identity header as a fallback.

Email

Preferred sources:

  1. claim_map.email
  2. common email claim
  3. username fallback

This fallback exists because current xyOps SSO requires both username and email to be present.

Recommendation:

  • Make sure your IdP returns a real email claim if you want a proper user email in xyOps.

Full name

Preferred sources:

  1. claim_map.full_name
  2. common name claim
  3. given_name + family_name
  4. email
  5. username

If your IdP does not provide a full name, that is fine. You can:

  • map full_name to its own header and let the plugin fall back automatically
  • or map full_name to the same header as username or email and let xyOps derive a prettier display name via cleanup_full_name

Groups, roles, and privileges

The plugin returns groups as a single trusted header, usually x-forwarded-groups.

xyOps then consumes that header through its normal SSO settings:

  • group_role_map
  • group_privilege_map
  • group_role_separator
  • replace_roles
  • replace_privileges

Example:

"header_map": {
	"groups": "x-forwarded-groups"
},
"group_role_map": {
	"devops": ["r12345"],
	"oncall": ["r67890"]
},
"group_privilege_map": {
	"platform-admins": ["admin"],
	"ticket-managers": ["edit_tickets", "delete_tickets"]
}

If the verified JWT contains:

{
	"groups": ["devops", "platform-admins"]
}

The plugin will emit:

"x-forwarded-groups": "devops,platform-admins"

Then xyOps will:

  • add role r12345
  • grant the admin privilege

Group separator

If your group claim is an array, the plugin joins it using your xyOps group_role_separator.

Example:

"group_role_separator": "|"

In that case the plugin will emit:

"x-forwarded-groups": "devops|platform-admins"

If your JWT claim is already a string, the plugin passes it through as-is. So if your IdP returns a string, make sure its delimiter matches the separator you configured in xyOps.

Performance and caching

To keep things fast, the plugin maintains a small on-disk cache keyed by:

  • the raw ALB JWT
  • the relevant SSO config

Default cache path:

/tmp/xyplug-sso-aws-alb-cache

The cache stores only:

  • the verified token expiration time
  • the generated trusted headers

The cache expires automatically when the JWT expires.

Security notes

  • The plugin validates the ALB JWT signature with aws-jwt-verify.
  • The plugin validates the ALB signer ARN.
  • The plugin validates the issuer.
  • The plugin validates the client ID unless you explicitly disable that by setting client_id to null.
  • If both x-amzn-oidc-identity and JWT sub are present, the plugin checks that they match.
  • The plugin does not perform IP/CIDR validation. xyOps still does that via SSO.whitelist.

Infrastructure recommendations:

  • Restrict your xyOps target so only the ALB can reach it.
  • Keep SSO.whitelist enabled.
  • Use HTTPS end-to-end where possible.

What this plugin does not do

  • It does not replace xyOps SSO user creation, session creation, or group mapping.
  • It does not manage logout behavior for AWS ALB auth cookies.
  • It does not validate source IPs. xyOps still does that.
  • It does not mutate any xyOps config on disk.

Troubleshooting

Login fails with a JWT verification error

Check:

  • jwt.aws_alb.alb_arn
  • jwt.aws_alb.issuer
  • jwt.aws_alb.client_id
  • network access to the AWS ALB public key endpoint

Groups are missing

Check:

  • that your IdP is actually returning the group claim
  • the jwt.claim_map.groups value
  • your xyOps group_role_separator

Usernames look strange

Remember that xyOps has its own username normalization rules.

If your OIDC sub contains characters outside the xyOps username character set, xyOps may normalize them. If you prefer friendlier usernames, map username to another claim that is stable in your environment.

Need more debugging output

On the xyOps side:

  • set debug_level to 9
  • inspect logs/SSO.log

On the plugin side:

  • set XYP_SSO_DEBUG=1 in the environment for the xyOps service to have the plugin write extra diagnostic information to STDERR

xyOps logs raw plugin STDOUT and STDERR at SSO debug level 9.

Local development

To syntax-check the CLI:

npm test

To run it manually with a saved JSON input:

cat sample-input.json | node index.js

The plugin expects the same XYWP payload that xyOps sends to custom SSO commands.

About

A xyOps SSO Plugin for authenticating users via AWS ALB OIDC.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors