grantcodes/indieauth-helper

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 of code_verifier to generate code_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 of code_verifier, so I updated getToken() to accept an extra_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:

https://glitch.com/edit/#!/veil-mirror?path=server.js:235:1

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, send code_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...