Skip to content

Commit 65a1dd7

Browse files
committed
fix(internet-identity): document available attribute keys and email vs verified_email
- list the unscoped attribute keys (name, email, verified_email) with guidance on when to use each: email for soft uses (mailing lists, contact), verified_email for access gating (admin allowlists) - document scopedKeys() resolution including the literal Microsoft provider URL (the {tid} segment is part of the URL, not a tenant placeholder) - add Mistake 10: substituting {tid} into the Microsoft URL silently breaks attribute lookups - add Mistake 11: treating email as verified — only verified_email carries the source provider's verification signal - new evals: Microsoft tid substitution, email-vs-verified_email for access gating
1 parent 95c5376 commit 65a1dd7

2 files changed

Lines changed: 50 additions & 0 deletions

File tree

evaluations/internet-identity.json

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,25 @@
7171
"Decodes msg_caller_info_data as a Candid-encoded ICRC-3 Value::Map and looks up the requested attribute key only after the signer check passes"
7272
]
7373
},
74+
{
75+
"name": "Adversarial: substituting tid in the Microsoft scoped-key URL",
76+
"prompt": "I'm using scopedKeys({ openIdProvider: 'microsoft' }) on the frontend. My Azure tenant ID is 11111111-2222-3333-4444-555555555555. How do I update the {tid} placeholder in the URL my backend looks for?",
77+
"expected_behaviors": [
78+
"Tells the user NOT to substitute the tenant GUID into the URL",
79+
"Explains that the {tid} part of https://login.microsoftonline.com/{tid}/v2.0 is a literal segment, not a placeholder, and that bundle keys arrive as openid:https://login.microsoftonline.com/{tid}/v2.0:<key> exactly",
80+
"Tells the backend to look up the literal key (e.g. openid:https://login.microsoftonline.com/{tid}/v2.0:email) without modification"
81+
]
82+
},
83+
{
84+
"name": "Adversarial: using email instead of verified_email for access gating",
85+
"prompt": "I have an admin allowlist of email addresses. I want to gate a sensitive update method on the caller's email being in that list. Show me how to request the email from II and check it on the backend.",
86+
"expected_behaviors": [
87+
"Requests verified_email (not just email) for access-gating use cases",
88+
"Explains that email is whatever the user's II-linked account reports while verified_email is only present when the source OpenID provider (e.g. Google) marked the email as verified and II surfaced that signal — only verified_email is trustworthy for authorisation",
89+
"On the backend, looks up implicit:nonce, implicit:origin, signer (against rdmx6-jaaaa-aaaaa-aaadq-cai), and the verified_email attribute key (or the openid:<provider>:verified_email scoped variant) before the allowlist check",
90+
"Does NOT recommend reading the email field as a substitute for verified_email"
91+
]
92+
},
7493
{
7594
"name": "Adversarial: build target suggestion",
7695
"prompt": "I'm getting 'Top-level await is not available in the configured target environment' when building my Vite frontend with II auth. How do I fix this?",

skills/internet-identity/SKILL.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ Internet Identity (II) is the Internet Computer's native authentication system.
4545

4646
9. **Reading attribute data without verifying the signer.** `msg_caller_info_data` (Rust) and `Prim.callerInfoData` (Motoko) return whatever bundle the caller provided. The IC verifies the signature, not the identity of the signer — any canister can produce a valid bundle. Check `msg_caller_info_signer` / `Prim.callerInfoSigner` against `rdmx6-jaaaa-aaaaa-aaadq-cai` (Internet Identity) before trusting any attribute, otherwise an attacker canister can forge attributes like `email = "admin@you.com"`.
4747

48+
10. **Substituting `{tid}` in the Microsoft scoped-key prefix.** The `microsoft` OpenID provider URL is the literal string `https://login.microsoftonline.com/{tid}/v2.0``{tid}` is part of the URL, not a tenant-ID placeholder you fill in. Bundle keys returned by `scopedKeys({ openIdProvider: 'microsoft' })` look like `openid:https://login.microsoftonline.com/{tid}/v2.0:email` exactly, and the backend must look up that literal key. Replacing `{tid}` with a tenant GUID will silently miss every attribute lookup.
49+
50+
11. **Treating `email` as verified.** `email` and `verified_email` are distinct keys. `email` is whatever the user's II-linked account reports; `verified_email` is only present when the source OpenID provider (e.g., Google) marked the email as verified, and II surfaces that signal through. Use `verified_email` for any access gating (admin allowlists, capability checks); use `email` only for soft uses like contact info or mailing lists. Request both for fallback behaviour: both are returned with the same value when the source provider marked the email as verified, only `email` when it didn't.
51+
4852
## Using II during local development
4953

5054
You have two choices for local development:
@@ -150,6 +154,33 @@ init();
150154

151155
When the backend needs more than the user's principal (e.g., a verified email), Internet Identity can return signed attributes alongside the delegation. The backend issues a nonce scoped to a specific action; the frontend requests the attributes during sign-in; the backend verifies the bundle when the user calls the protected method.
152156

157+
#### Available attribute keys
158+
159+
`requestAttributes({ keys })` accepts the following keys:
160+
161+
| Key | Meaning | When to use |
162+
|---|---|---|
163+
| `name` | The user's display name. | Personalisation in the UI. |
164+
| `email` | The user's email as reported by their II linked account. | Mailing-list signups, contact email, anything where you don't gate access on the email. |
165+
| `verified_email` | Same value as `email`, but only present when the source OpenID provider (e.g., Google) marked the email as verified, and II surfaces that signal. | Access gating (e.g. an admin allowlist by email). Treat this as the only trustworthy email for authorisation. |
166+
167+
Request both `email` and `verified_email` if you want fallback behaviour: when the source provider marked the email as verified, both keys are present with the same value; when it didn't, only `email` is returned.
168+
169+
`scopedKeys({ openIdProvider, keys? })` rewrites the keys above into provider-scoped keys of the form `openid:<provider-url>:<key>`, so II returns the values from the linked OpenID account directly (with implicit consent, no extra prompt). Provider URLs:
170+
171+
| Provider | URL prefix in the bundle keys |
172+
|---|---|
173+
| `'google'` | `openid:https://accounts.google.com:` |
174+
| `'apple'` | `openid:https://appleid.apple.com:` |
175+
| `'microsoft'` | `openid:https://login.microsoftonline.com/{tid}/v2.0:` (the `{tid}` part is literal: do not substitute a tenant ID into it) |
176+
177+
`keys` defaults to `['name', 'email', 'verified_email']`. Examples:
178+
179+
- `scopedKeys({ openIdProvider: 'google' })` &rarr; `['openid:https://accounts.google.com:name', 'openid:https://accounts.google.com:email', 'openid:https://accounts.google.com:verified_email']`
180+
- `scopedKeys({ openIdProvider: 'google', keys: ['email'] })` &rarr; `['openid:https://accounts.google.com:email']`
181+
182+
The same `email` vs `verified_email` rule applies to scoped keys: use the verified variant when the email gates access.
183+
153184
```javascript
154185
import { AuthClient } from "@icp-sdk/auth/client";
155186
import { AttributesIdentity } from "@icp-sdk/core/identity";

0 commit comments

Comments
 (0)