Domain access and join requests¶
With OIDC login, people with no account can get in without an administrator creating it first. A domain rule signs up whoever arrives with a verified e-mail of that domain and puts them in a group, so they land in Home with the group's permissions. Anyone else can ask to join from the screen they land on, and an administrator approves or denies the request in Users. Both work the same in Free and Pro. GitHub login signs nobody up: a GitHub login doesn't prove an e-mail address.
A person's first sign-in¶
kubelatch looks for the person's account in this order, and stops at the first match:
- The provider's identity is already linked to an account: it signs in.
- An account has the e-mail address: it links by e-mail, as
OIDC_AUTOLINKallows (How accounts are linked). Only enabled accounts count; if the only ones are disabled, or there are two, the sign-in is refused as before (disabled,ambiguous_account). - No account has the address, not even through an identity of the provider: if the domain has a rule, kubelatch creates the account and signs it in; if it doesn't, or the rule can't sign the person up, the browser goes to Ask to join.
The address has to pass the usual checks first: the provider vouches for it and its domain is in OIDC_ALLOWED_DOMAINS, or the sign-in ends in email_unverified or domain_not_allowed, before any rule or request. An account that holds the address through its identity alone, with another identity at the provider, gets identity_mismatch, as in When someone leaves, or an address comes back: a reassigned address never creates an account beside the old one, and never reaches a request. Those cases are for an administrator to look at.
Domain access¶
A rule names an e-mail domain of OIDC_ALLOWED_DOMAINS, the group the new people join and, optionally, how long that membership lasts. The group carries the permissions: what you change in the group reaches everyone the rule brought in, as for any member (Groups).
Everyone in the domain gets the group's permissions
A rule hands the group's permissions to anyone in the company who signs in for the first time. Point it at a group with a modest role, such as viewer on the namespaces everyone needs, and grant the rest by hand. Groups never carry cluster-admin.
Add a domain¶
- In Users, the Domain access card lists the rules. It only shows while OIDC login is on. Click Add domain.
- Under Domain, choose one of
OIDC_ALLOWED_DOMAINSthat has no rule yet. Exact domains, as in the variable: a subdomain is another domain. - Under Joins the group, choose Existing and the group, or New group: Group name, its Group id (proposed from the name, editable until you save, never reused) and, if you like, its First permission (optional), with the same form as the group permissions.
- Under Membership, choose No expiry, 30 days, 90 days or Other with its Days: how long each new person's membership lasts, counted from their sign-up. The group's permissions keep their own expiries.
- Click Save. The notice says Rule of {domain} created.
With New group, the group, its first permission and the rule are created together: a permission the rules refuse leaves no group behind. A domain goes to one group; to send it to another, remove its rule and add a new one.
What each new person gets¶
- An account, with the login taken from the part of the address before the
@: in lower case, with each character other than a letter fromatoz, a digit,.,_or-turned into-, and any.,_or-at its start dropped.Ana.Perez@acme.combecomesana.perez. If an account already has that login, enabled or not, it takes<login>.<domain>(ana.perez.acme.com); if that one is taken too, nothing is created and the person goes to Ask to join, where you choose the login. The name comes from the provider, and the account's e-mail is the provider's address. - Its identity linked: in Users, Sign-in reads "{name} · by domain".
- The membership, marked "domain {domain}" on the group's page, and the group's permissions derived from it. Those permissions name whoever created the rule as their grantor, because that person decided the domain gets them.
Logins go first come, first served across every domain of OIDC_ALLOWED_DOMAINS, by rule or by request: bob@contractor.io may get bob before bob@acme.com signs in. On a cluster whose identity template uses {login} and binds users by name, use {email} or {oidc_subject} instead.
Before creating anything, kubelatch checks the group's permissions again, as it does when you add a member by hand. If one no longer passes, for example because its namespace lost the PSA label, or a cluster doesn't answer, nobody is signed up: the person goes to Ask to join. Fix or revoke that group permission.
The rules table¶
Each row has its Domain, the group it Joins the group with what that group gives, the Membership ("no expiry" or "30 days from sign-up") and how many people Signed up through it. A domain that left OIDC_ALLOWED_DOMAINS carries the pill outside the configuration: the rule does nothing until the domain comes back.
The row menu has:
- Change expiry: the membership expiry of the people who sign up from now on. Those already in keep theirs; change it on the group's page (Extend).
- Remove: after confirming, it removes the rule and withdraws the memberships it added that are still live, with the permissions they derive. The accounts stay enabled, without those permissions, and the members you added by hand stay. A rule only acts when someone signs up: a member you remove by hand isn't added back.
A group a rule points at can't be deleted until you remove the rule (409 signup_domain.group_in_use).
Free's limit of people¶
Each account a rule creates takes a seat. In Free, once the five people are there, the sixth isn't signed up: they land on Ask to join with "kubelatch is at its limit of people; an administrator will have to free a seat or extend the edition.", and they can send their request. Approving it answers 422 edition.limit until a seat is free (Editions and license). Two first sign-ins at the same time never take more seats than there are.
Join requests¶
What the person sees¶
Ask to join says "You signed in with {provider} as {address}, but no kubelatch account has that address." It shows the name the provider sent, read-only, an optional Reason of up to 500 characters, the access contact (KUBELATCH_ACCESS_CONTACT) and Try another account. Send request leaves it pending:
| State | What the page says |
|---|---|
| Request pending | When they asked and when the request expires if nobody decides: 30 days later. |
| Request denied | The date, and the contact. Never your note. |
| Approved | Nothing to wait for: their next sign-in gets them in. |
| Expired | The form again, with "Your last request expired without an answer. You can ask again." |
The person never types their address: it comes from the provider, in a signed cookie that lasts ten minutes and that sending the request consumes. To see how their request stands later, they sign in again with Sign in with {name}.
A person can have one pending request, and the instance at most 100 (429 join.too_many past them); the page shares the login's limit per IP. A request takes no seat until you approve it.
Approve or deny¶
The Join requests card, in Users and only with OIDC login on, lists the pending ones with "N pending": the person's name, the address, the provider, when they asked and their reason. Notices, in the top bar, says "1 person is asking to join kubelatch." and leads there.
- Approve… opens Approve {name}. Login comes suggested from the address, by the rule above; a login never changes afterwards. Under Initial access, choose Group and the group, Direct permission with the usual permission form, or None. With a limit of people, a line says which seat it takes: "It takes a seat: 4 of 5.". Approve creates the account, links its identity ("{name} · by request" in Sign-in) and gives that access, all at once or not at all: if the group or the permission is refused, nothing is created and the request stays pending. A login already taken marks the field.
- Deny asks first, with an optional Internal note. The person sees that it was denied and the access contact, not your note. A denied person can ask again.
If someone created an account with that address in the meantime, approving answers 409 join.email_taken: link that account instead. A request whose person got in another way (a new rule, an account matched by e-mail, a link) is cancelled by itself. One that waits 30 days expires, and the person can ask again. The card lists only pending requests: decided, cancelled and expired ones stay in the control log.
In the audit log¶
Every step leaves its row in Audit → Control plane (The control plane):
| What happened | Rows |
|---|---|
| A domain sign-up | user.create with via: domain and the domain, and no actor; oidc.link with via: domain; group.member.add with no actor and rule, the domain; one grant.create per derived permission, with the rule's creator as actor and rule; login.success. |
| A sign-up that didn't happen | login.failure with reason: unknown_account and, when a rule applied, signup_refused: seat_limit, login_taken or group_check. |
| A request | access_request.create, with no actor, the address, the issuer and the provider's subject. |
| An approval | user.create with via: join and the request_id; oidc.link with via: join; the group's or the permission's own rows; access_request.approve with the address, the login and the access given. |
| A denial | access_request.deny with the address. The note is in the request, not in the row. |
| A request cancelled | access_request.cancel, with how the person got in (via). |
| A rule | signup_domain.create, signup_domain.update (from and to, in seconds) and signup_domain.delete (members_removed), with one group.member.remove per membership withdrawn, with rule. |