support for PKCE
martymcguire opened this issue · 8 comments
Proof-Key for Code Exchange protects against intercepted authorization codes during the OAuth flow.
I was able to add it to the indieauth helper WIP by:
- generating a random string
code_verifier
and storing it in the session for the user - computing the
sha256
hash ofcode_verifier
to generatecode_challenge
- got the auth redirect URL via
getAuthUrl()
, then adding&code_challenge=...&code_challenge_method=S256
to the string before redirecting. - when exchanging the
code
for a token, you need to pass along the original value ofcode_verifier
, so I updatedgetToken()
to accept anextra_args = {}
Object.
you can see the changes here:
https://glitch.com/edit/#!/veil-mirror?path=lib/indieauth-authentication.js:198:1
and the usage of it to add code_challenge to the auth url:
https://glitch.com/edit/#!/veil-mirror?path=server.js:198:1
and the extra args to getToken:
PKCE support could get pushed into the lib itself.
e.g. if this.options.code_verifier
exists...
- when calling
getAuthUrl
, generate the hash and send the extra args. - when calling
getToken
, sendcode_verifier
argh accidental close
Ok trying to get my head around this now, and think I more or less understand it.
I think ideally I'd like to just do this by default without any user input if possible...
But I am not sure on a few points:
- If the PKCE url parameters are sent to auth / token endpoints that don't support them they will just be ignored hopefully? That means they can just always be sent without any danger?
- Since a
secret
option is already used for the state generation could we not just use that to dynamically generate a code verifier? - But to do it totally automatically would require there not to be any idea of a session so the code verifier would need to be able to be replicated if the instance has the same options set.
I think all of that is definitely possible but may not actually bring about a huge security benefit as I don't think it would be possible to use a truly random string without saving it in a session to get reused.
OAuth2 servers that don't understand PKCE should ignore the extra code_*
parameters, as I understand it. That's part of its design as an extension of OAuth2.
I'm sure it's possible to create a code_verifier
value from existing secrets without having to store something in the session, but I don't know what would be suitable. For my use case, where session
is just a lookup key, it was pretty easy to just generate another random string.
Well a combination of this.options.secret
and this.options.me
will be unique for every user of an app, but would be the same every time someone logged in with the same url, which may defeat the point?
"The PKCE extension does not add any new responses, so clients can always use the PKCE extension even if an authorization server does not support it." - https://www.oauth.com/oauth2-servers/pkce/authorization-code-exchange/
So it would be fine to send it by default.
These docs are pretty clear on what a code verifier should be:
This is a cryptographically random string using the characters A-Z, a-z, 0-9, and the punctuation characters -._~ (hyphen, period, underscore, and tilde), between 43 and 128 characters long.
From: https://www.oauth.com/oauth2-servers/pkce/authorization-request/
This has me leaning towards requiring the client app to create and store it, passing it to the indieauth-helper (which just won't send it if it's not there).
Ok I added initial (non automatic) support.
There is also a method to generate a random string that can be used for the code verifier:
const auth = new IndieAuth(options)
const codeVerifier = auth.generateRandomString()
// Save the code verifier to the session or something
auth.options.codeVerifier = codeVerifier
Not tested in any way...