Skip to content

Commit 9586136

Browse files
authored
Treat a missing aud claim as an empty list (#3542)
# Description of Changes The `aud` claim is required in the OIDC spec, but the server currently allows it to be missing, and the spacetime auth tokens we use for the website don't have an audience. Previously the module bindings would throw an error if there were no `aud` claim in a jwt payload (if someone used the `audience` within a reducer), but this change makes us treat a missing audience as an empty list. This also renames the `authCtx` fields to `senderAuth` in the typescript and csharp module APIs, so they match rust. # API and ABI breaking changes This doesn't break any ABIs. This changes the ReducerContext APIs in typescript and rust, but only by renaming a field that hasn't been released yet. # Expected complexity level and risk 1. # Testing I've tested accessing the `audience` within a reducer for a token missing an `aud` claim in Typescript and Rust.
1 parent b2cee93 commit 9586136

7 files changed

Lines changed: 15 additions & 10 deletions

File tree

crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module#FFI.verified.cs

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/bindings-csharp/Codegen.Tests/fixtures/server/snapshots/Module#FFI.verified.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public sealed record ReducerContext : DbContext<Local>, Internal.IReducerContext
1414
public readonly ConnectionId? ConnectionId;
1515
public readonly Random Rng;
1616
public readonly Timestamp Timestamp;
17-
public readonly AuthCtx AuthCtx;
17+
public readonly AuthCtx SenderAuth;
1818

1919
// We need this property to be non-static for parity with client SDK.
2020
public Identity Identity => Internal.IReducerContext.GetIdentity();
@@ -30,7 +30,7 @@ Timestamp time
3030
ConnectionId = connectionId;
3131
Rng = random;
3232
Timestamp = time;
33-
AuthCtx = AuthCtx.BuildFromSystemTables(connectionId, identity);
33+
SenderAuth = AuthCtx.BuildFromSystemTables(connectionId, identity);
3434
}
3535
}
3636

crates/bindings-csharp/Codegen/Module.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1111,7 +1111,7 @@ public sealed record ReducerContext : DbContext<Local>, Internal.IReducerContext
11111111
public readonly ConnectionId? ConnectionId;
11121112
public readonly Random Rng;
11131113
public readonly Timestamp Timestamp;
1114-
public readonly AuthCtx AuthCtx;
1114+
public readonly AuthCtx SenderAuth;
11151115
11161116
// We need this property to be non-static for parity with client SDK.
11171117
public Identity Identity => Internal.IReducerContext.GetIdentity();
@@ -1121,7 +1121,7 @@ internal ReducerContext(Identity identity, ConnectionId? connectionId, Random ra
11211121
ConnectionId = connectionId;
11221122
Rng = random;
11231123
Timestamp = time;
1124-
AuthCtx = AuthCtx.BuildFromSystemTables(connectionId, identity);
1124+
SenderAuth = AuthCtx.BuildFromSystemTables(connectionId, identity);
11251125
}
11261126
}
11271127

crates/bindings-csharp/Runtime/JwtClaims.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ private List<string> ExtractAudience()
6767
{
6868
if (!RootElement.TryGetProperty("aud", out var aud))
6969
{
70-
throw new InvalidOperationException("JWT missing 'aud' claim");
70+
return [];
7171
}
7272

7373
return aud.ValueKind switch

crates/bindings-typescript/src/server/reducers.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ export type ReducerCtx<SchemaDef extends UntypedSchemaDef> = Readonly<{
116116
timestamp: Timestamp;
117117
connectionId: ConnectionId | null;
118118
db: DbView<SchemaDef>;
119-
authCtx: AuthCtx;
119+
senderAuth: AuthCtx;
120120
}>;
121121

122122
/**

crates/bindings-typescript/src/server/runtime.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ class JwtClaimsImpl implements JwtClaims {
8585
}
8686
get audience() {
8787
const aud = this.fullPayload['aud'];
88+
if (aud == null) {
89+
return [];
90+
}
8891
return typeof aud === 'string' ? [aud] : (aud as string[]);
8992
}
9093
}
@@ -193,7 +196,7 @@ export const hooks: ModuleHooks = {
193196
timestamp: new Timestamp(timestamp),
194197
connectionId: ConnectionId.nullIfZero(new ConnectionId(connId)),
195198
db: getDbView(),
196-
authCtx: AuthCtxImpl.fromSystemTables(
199+
senderAuth: AuthCtxImpl.fromSystemTables(
197200
ConnectionId.nullIfZero(new ConnectionId(connId)),
198201
senderIdentity
199202
),

crates/bindings/src/lib.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1219,7 +1219,9 @@ impl JwtClaims {
12191219
}
12201220

12211221
fn extract_audience(&self) -> Vec<String> {
1222-
let aud = self.get_parsed().get("aud").unwrap();
1222+
let Some(aud) = self.get_parsed().get("aud") else {
1223+
return Vec::new();
1224+
};
12231225
match aud {
12241226
serde_json::Value::String(s) => vec![s.clone()],
12251227
serde_json::Value::Array(arr) => arr.iter().filter_map(|v| v.as_str().map(String::from)).collect(),

0 commit comments

Comments
 (0)