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
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,9 @@ served from the application's own host
With `spec.question` it asks one question at the door, and the `demo:` group of
the answer joins `audienceGroup` in the participant's token
([install guide](docs/install.md#ask-one-question-at-the-door-optional)).
`spec.browserGroups` adds groups naming the browser and platform a participant
joined with, such as `demo:browser-safari`
([install guide](docs/install.md#put-the-browser-in-the-groups-optional)).
Required cookie Secret:

```sh
Expand Down
27 changes: 27 additions & 0 deletions api/v1alpha1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,14 @@ type RoomSpec struct {
// event.
// +optional
Question *RoomQuestion `json:"question,omitempty"`
// BrowserGroups adds two groups to everyone who joins, named after the
// browser and the platform they joined with, such as demo:browser-safari
// and demo:platform-ios. Empty, nothing is added.
//
// Like an answer, they are kept on the Participant (spec.groups) at
// enrollment and are only what the browser says it is.
// +optional
BrowserGroups *BrowserGroups `json:"browserGroups,omitempty"`
// EndsAt can be extended without changing participant identities.
EndsAt metav1.Time `json:"endsAt"`
// +kubebuilder:default=Closed
Expand Down Expand Up @@ -185,6 +193,25 @@ type RoomAnswer struct {
Group string `json:"group"`
}

// BrowserGroups names the two groups that say how a participant joined:
// <prefix>browser-<browser> and <prefix>platform-<platform>.
//
// Both names come from fixed lists, whatever the browser sends. Browsers:
// safari, chrome, firefox, edge, samsung, opera, other. Platforms: ios,
// android, macos, windows, chromeos, linux, other. A forged User-Agent can
// only pick another name from them, and anyone can claim to be Safari, so
// bind these groups only to what anyone may have.
//
// There is no phone brand: Chrome on Android no longer sends one. ios is the
// closest there is to Apple.
type BrowserGroups struct {
// Prefix starts both names: demo: gives demo:browser-safari, and
// demo:my-talk: gives demo:my-talk:browser-safari.
// +kubebuilder:validation:MaxLength=100
// +kubebuilder:validation:Pattern="^demo:([a-zA-Z0-9][a-zA-Z0-9:_-]*)?$"
Prefix string `json:"prefix"`
}

type Code struct {
// +kubebuilder:validation:MaxLength=12
Code string `json:"code"`
Expand Down
20 changes: 20 additions & 0 deletions api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 19 additions & 0 deletions config/crd/bases/room-pass.koudijs.dev_rooms.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,25 @@ spec:
x-kubernetes-validations:
- message: audienceGroup is immutable
rule: self == oldSelf
browserGroups:
description: |-
BrowserGroups adds two groups to everyone who joins, named after the
browser and the platform they joined with, such as demo:browser-safari
and demo:platform-ios. Empty, nothing is added.

Like an answer, they are kept on the Participant (spec.groups) at
enrollment and are only what the browser says it is.
properties:
prefix:
description: |-
Prefix starts both names: demo: gives demo:browser-safari, and
demo:my-talk: gives demo:my-talk:browser-safari.
maxLength: 100
pattern: ^demo:([a-zA-Z0-9][a-zA-Z0-9:_-]*)?$
type: string
required:
- prefix
type: object
endsAt:
description: EndsAt can be extended without changing participant identities.
format: date-time
Expand Down
5 changes: 5 additions & 0 deletions deploy/example/room.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ spec:
# group: demo:framework-react
# - label: Svelte
# group: demo:framework-svelte
# Optional: groups naming the browser and platform each participant joined
# with, such as demo:browser-safari and demo:platform-ios. Only what the
# browser claims to be.
# browserGroups:
# prefix: "demo:"
# Nobody can join after this. Move it before every event.
endsAt: "2026-12-31T18:00:00Z"
enrollment: Open
Expand Down
29 changes: 29 additions & 0 deletions docs/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,35 @@ Somebody who picks Svelte signs in with the groups `demo:my-talk` and
- **Set it before you go on stage.** Editing it starts a fresh code epoch, and
anyone who joined before the question existed is not asked it.

### Put the browser in the groups (optional)

`spec.browserGroups` adds two groups to everyone who joins, named after the
browser and the platform they joined with. Nobody is asked anything; the join
page says what it will share ("and your browser, Safari on iOS").

```yaml
spec:
browserGroups:
prefix: "demo:" # demo:browser-safari, demo:platform-ios
```

| Group | Names |
|---|---|
| `<prefix>browser-…` | `safari`, `chrome`, `firefox`, `edge`, `samsung`, `opera`, `other` |
| `<prefix>platform-…` | `ios`, `android`, `macos`, `windows`, `chromeos`, `linux`, `other` |

- **It is what the browser says it is.** Room Pass reads the User-Agent and
only ever picks a name from these lists, so a forged one gains nothing but
another name from them. Anyone can claim to be Safari, so bind these groups
only to what anyone may have.
- **There is no phone brand.** Chrome on Android stopped sending one when the
User-Agent was frozen; `platform-ios` is the closest there is to "Apple".
- **The prefix keeps them apart.** `demo:my-talk:` gives
`demo:my-talk:browser-safari`. It must start with `demo:`, like every group
here.
- **They are kept on the Participant** in `spec.groups` beside the answer, from
the browser that enrolled. Like the question, set it before anyone joins.

## 6. Connect your application

Your application is an OIDC client of the issuer:
Expand Down
4 changes: 2 additions & 2 deletions internal/server/metrics_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ func TestMetricsReflectOutcomesWithoutIdentityLabels(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if _, err := s.enrollParticipant(context.Background(), "WRONG", "private-name", ""); err == nil {
if _, err := s.enrollParticipant(context.Background(), "WRONG", "private-name", "", ""); err == nil {
t.Fatal("invalid code accepted")
}
if _, err := s.enrollParticipant(context.Background(), "BCDFGH", "private-name", ""); err != nil {
if _, err := s.enrollParticipant(context.Background(), "BCDFGH", "private-name", "", ""); err != nil {
t.Fatal(err)
}
for _, path := range []string{"/healthz", "/unknown-private-name?code=SECRET", "/another-random-path"} {
Expand Down
Loading
Loading