diff --git a/primer/index.bs b/primer/index.bs
index 5e863e8..c104d8a 100644
--- a/primer/index.bs
+++ b/primer/index.bs
@@ -360,18 +360,27 @@ id, the [=code challenge=], the user's webid, their desired response types, and
}
```
-
11. Send authorization code to redirect url
+11. Send authorization code and `iss` parameter to redirect url
Once Alice successfully logs in, the OP redirects back to the application via the provided
-redirect uri, including useful information with it:
+redirect uri. The OP includes the authorization code as well as its own issuer identifier as the iss parameter to protect against mix-up attacks:
```http
-302 redirect to: https://decentphotos.example/callback?code=m-OrTPHdRsm8W_e9P0J2Bt
+302 redirect to: https://decentphotos.example/callback?code=m-OrTPHdRsm8W_e9P0J2Bt&iss=https%3A%2F%2Fsecureauth.example
```
-This redirect gives decentphotos the [=authorization code=] that it will exchange for requested tokens.
+This redirect gives decentphotos the [=authorization code=] that it will exchange for requested tokens, along with the issuer identifier required for the next validation step.
-12. Generates a DPoP Client Key Pair
+12. Validate `iss` Parameter
+
+Before proceeding to exchange the code for tokens, decentphotos must verify the `iss` parameter.
+This ensures the application is receiving the code from the exact OP it intended to communicate with.
+
+The application checks that the `iss` value (`https://secureauth.example`) matches the issuer URL it resolved in Step 3.
+If the `iss` parameter is missing or does not match the expected OP, decentphotos must reject the response, abort the authentication flow, and optionally display an error to Alice.
+In our example, the issuer matches, so we may safely continue.
+
+13. Generates a DPoP Client Key Pair
Solid-OIDC depends on
[Demonstration of Proof-of-Possession (DPoP) tokens](https://tools.ietf.org/html/draft-ietf-oauth-dpop).
@@ -410,7 +419,7 @@ The public key looks like:
}
```
-13. Generates a DPoP Header
+14. Generates a DPoP Header
Now that we generated a private key for the client, we need to generate the DPoP header. To do
so, we create a [JSON Web Token](https://jwt.io/introduction/) and sign it using the key we
@@ -459,7 +468,7 @@ Token Body:
that can optionally be used by the server to defend against replay attacks
- `"iat": 1603306128`: The date the token was issued, in this case October 21, 2020 15:52:33 GMT.
-14. Token request with code and code verifier
+15. Token request with code and code verifier
Now, we have everything we need to make an auth request. No need to redirect the web browser
for this one. We only need to make an AJAX request to the `token` endpoint as defined in the
@@ -495,7 +504,7 @@ Body:
Once this request is completed decentphotos can remove the code verifier from session storage.
-15. Validate code verifier
+16. Validate code verifier
The OP looks up the [=authorization code=] that was saved earlier in a keystore. It checks to see that the client
id in the keystore corresponds to the client id from the request. If it does not, it must
@@ -511,13 +520,13 @@ BASE64URL-ENCODE(SHA256(ASCII(code_verifier))) == code_challenge
If they do not correspond the OP must reject the request with a 400 HTTP status and `invalid_grant`
error code.
-16. Validates DPoP Token Signature
+17. Validates DPoP Token Signature
The OP extracts the client's public key from the DPoP header (at header.jwk). It confirms that
the DPoP token has a valid signature. If not, the OP must reject the request with a 400 HTTP
status and `invalid_dpop_proof` error code.
-17. Converts the DPoP public key to a JWK thumbprint
+18. Converts the DPoP public key to a JWK thumbprint
Currently the DPoP token contains a JWK public key, but before we place it inside the access
token, it needs to be converted into a [JWK thumbprint](https://tools.ietf.org/html/rfc7638).
diff --git a/primer/primer-login.mmd b/primer/primer-login.mmd
index 4ba4eee..2a011e9 100644
--- a/primer/primer-login.mmd
+++ b/primer/primer-login.mmd
@@ -17,13 +17,14 @@ sequenceDiagram
note over OP: 8. Validates redirect url with Client ID Document
note over OP: 9. Alice Logs In
note over OP: 10. Generates an authorization code
- OP->>Client: 11. Send authorization code to redirect url
- note over Client: 12. Generates DPoP Client Key Pair
- note over Client: 13. Generates a DPoP Header
- Client->>OP: 14. Token request with code and code verifier
- note over OP: 15. Validates code verifier
- note over OP: 16. Validates DPoP Token signature
- note over OP: 17. Converts the DPoP public key to a JWK thumbprint
+ OP->>Client: 11. Send authorization code and iss parameter to redirect url
+ note over Client: 12 Validates that the returned iss matches the expected OP issuer
+ note over Client: 13. Generates DPoP Client Key Pair
+ note over Client: 14. Generates a DPoP Header
+ Client->>OP: 15. Token request with code and code verifier
+ note over OP: 16. Validates code verifier
+ note over OP: 17. Validates DPoP Token signature
+ note over OP: 18. Converts the DPoP public key to a JWK thumbprint
note over OP: 19. Generates id token
note over OP: 20. Generates refresh token
- OP->>Client: 21. Send tokens
+ OP->>Client: 21. Send tokens
\ No newline at end of file
diff --git a/sequence.mmd b/sequence.mmd
index 97c0b36..0b93e74 100644
--- a/sequence.mmd
+++ b/sequence.mmd
@@ -14,7 +14,8 @@ sequenceDiagram
OP->> ClientID: get Client ID document
ClientID->> OP: ClientID document
Note over OP: compare redirect_uri
- OP ->> C: return Authorization Code
+ OP ->> C: return Authorization Code and iss parameter
+ Note over C: Validate iss matches expected OP
C ->> OP: present Authorization Code and DPoP proof
Note over OP: ⚙️ Client is authenticated ✅
OP ->> C: return DPoP bound OIDC ID Token