openid / AppAuth-JS

JavaScript client SDK for communicating with OAuth 2.0 and OpenID Connect providers.
Apache License 2.0
985 stars 161 forks source link

PKCE support #28

Closed stupiduglyfool closed 6 years ago

stupiduglyfool commented 7 years ago

The readme specifies:

https://github.com/openid/AppAuth-JS/blob/master/README.md

The library also supports the PKCE extension to OAuth

However I cannot find any specification of code_challenge within the source code, therefore I would like some guidance of how to enable this?

For example if I look at the similarly named project AppAuth-Android I do find references to this expected PKCE parameter:

https://github.com/openid/AppAuth-Android/blob/de0ed1ad9b2362c1b27dfb3c1d2e6c8f13d699b4/library/java/net/openid/appauth/AuthorizationRequest.java#L959

Thanks.

tikurahul commented 7 years ago

So currently, PKCE is supported via additional parameters 1, 2 that you can pass to both the AuthorizationRequest and the TokenRequest. I will update the library to add first class support for it.

stupiduglyfool commented 7 years ago

If it helps anyone I created a CodeVerifier for now: codeverifier-ts

Then provided the results to tokenHandler.performTokenRequest, and authorizationHandler.performAuthorizationRequest. eg: performauthorizationrequest-ts

tikurahul commented 7 years ago

Thanks for sharing your code snippet. This works in the context of Node / Electron. We will need WebCrypto in the browser based implementation (which requires the use of a secure context). I am going to make that a part of the library.

Jenan commented 6 years ago

Is there any sample for PKCE? What package of crypto is available for this purpose? Thx

tikurahul commented 6 years ago

crypto is a part of Node. For the web, you should be able to use WebCrypto as long as it’s a secure context.

Jenan commented 6 years ago

In electron app is possible to use nodejs crypto package or for website? Thx

tikurahul commented 6 years ago

Yes. Electron has access to all Node APIs. Electron can use crypto.

nmocruz commented 6 years ago

any sample with WebCrypto ??? I was trying to use @stupiduglyfool sample with WebCrypto but is not working well, is not producing a valid pair to use against identity server 4

tikurahul commented 6 years ago

I can add some support for it.

someone1 commented 6 years ago

My apologies if I'm wrong here, but I think the example given by @stupiduglyfool as-is will pass the code_verifier in the initial auth request, thus leaking the value that should otherwise be kept hidden. I think instead, for PKCE, you need to manage the challenge/verifier outside this package, as in store the verifier on your own (e.g. localStorage) and know to pass it in to the token request after receiving a code to initiate the exchange with.

Here is how I have PKCE generated and working in the browser (doesn't work with Google OpenID Connect as PKCE is only supported for native applications that redirect to a custom scheme or localhost address):

import { cryptoGenerateRandom, } from '@openid/appauth';
import TextEncodingShim from 'text-encoding'; // shimmed with 'text-encoding' package
const crypto = window.crypto; // shimmed with the 'webcrypto-shim' package

// Based off of the 'google-auth-library-nodejs' package: https://github.com/google/google-auth-library-nodejs/blob/master/src/auth/oauth2client.ts
async function generateCodeVerifier() {
    const codeVerifier = cryptoGenerateRandom(128);
    const codeChallenge = base64ArrayBuffer(await sha256(codeVerifier))
      .split('=')[0]
      .replace(/\+/g, '-')
      .replace(/\//g, '_');
    return { codeVerifier, codeChallenge };
  }

export function sha256(str: string) {
  // We transform the string into an arraybuffer.
  return crypto.subtle.digest('SHA-256', new TextEncoder('utf-8').encode(str)).then((buffer) => {
    return buffer;
  });
}

// base64ArrayBuffer taken from: https://gist.github.com/jonleighton/958841
stupiduglyfool commented 6 years ago

@someone1 Thats correct (spec: rfc7636),

In the performAuthorizationRequest you just send the code_challenge + code_challenge_method.

In the subsequent token request you send the code_verifier.

Updated example

twinklekumarp commented 6 years ago

I am struggling to work AppAuth-Js with Identityserver3 to implement AuthorizationCode with PKCE. I am able to launch Login page but after successful login it is not redirecting but coming back to login page only. If you have completed your ionic example with PKCE can you please share it? There are so many days i have spent but totally stuck..please

stupiduglyfool commented 6 years ago

I have it working with IdentityServer, how have you configured it, you should have a Client with a RedirectUri, the IdentityServer log may be able to provide useful information.

nmocruz commented 6 years ago

Can you share the way that you generate the pair… I was getting problems

A 9h33 sex, 11 de Mai de 2018, stupiduglyfool notifications@github.com escreveu:

I have it working with IdentityServer, how have you configured it, you should have a Client with a RedirectUri, the IdentityServer log may be able to provide useful information.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/openid/AppAuth-JS/issues/28#issuecomment-388298818, or mute the thread https://github.com/notifications/unsubscribe-auth/AA5o5XHS8aEKMV18Q_VZ8iJ6C8i97WtZks5txUzEgaJpZM4P1ioy .

stupiduglyfool commented 6 years ago

You need to add a client to IdentityServer here is an example:

IdentityServer Example:

new Client { ClientName = "Native Client (Code Flow with PKCE)", ClientId = "NativeClient", Enabled = true, RequireConsent = true, Flow = Flows.AuthorizationCodeWithProofKey, ClientSecrets = new List { new Secret("non-secret-secret".Sha256()) }, AllowedCorsOrigins = _ioc.CoreModel.Service().GetCorsOrigins().ToList(), PostLogoutRedirectUris = new List { $"https://{ServicePrefixConstants.Www}.{domainName}/#/logout" }, RedirectUris = new List { $"http://127.0.0.1:8000" }, AllowedScopes = new List { StandardScopes.OfflineAccess.Name, "api" } , RefreshTokenUsage = TokenUsage.ReUse, AccessTokenType = AccessTokenType.Jwt, AccessTokenLifetime = (int)TimeSpan.FromDays(1).TotalSeconds }

In IdentityServer3 its not possible to have a client without a secret even though this flow doesn't require it.. (IdentityServer4 fixes this), thus you need to supply that secret with the request on the client..

typescript:

//required in electron if you're using self signed certs.. process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";

let config = { "clientId": "NativeClient" "clientSecret": "non-secret-secret", "openidUri": "https://localhost:5000", "redirectUri": "http://127.0.0.1:8000", "scope": "openid api offline_access" }

new AuthFlow(config)

twinklekumarp commented 6 years ago

the example you showed is i think for AuthorizationCode flow, I am looking for AuthroizationWithProofKey flow. Moreover, what is openidUri?

stupiduglyfool commented 6 years ago

What part of the my example makes you think it is for AuthorizationCode flow?

Flow = Flows.AuthorizationCodeWithProofKey,

opeiduri is the uri for your identity server.

The AuthFlow typescript sample is based on the electron example repository that goes along with this project that demonstrates how to do pkce with electron:

appauth-js-electron-sample

glerchundi commented 6 years ago

@tikurahul it would be more than interesting if you could update the library with a full example, do you have a ETA for this?

Thanks!

tikurahul commented 6 years ago

@glerchundi Sorry have been swamped with other things the last couple of weeks. I am going to try to get to this soon.

glerchundi commented 6 years ago

Thanks @tikurahul!

On Fri, 24 Aug 2018 at 21:20, Rahul Ravikumar notifications@github.com wrote:

@glerchundi https://github.com/glerchundi Sorry have been swamped with other things the last couple of weeks. I am going to try to get to this soon.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/openid/AppAuth-JS/issues/28#issuecomment-415857737, or mute the thread https://github.com/notifications/unsubscribe-auth/ACIPlvMaiwvfDrVnZulcrxc2upBfEjFBks5uUFIDgaJpZM4P1ioy .

mraible commented 6 years ago

Hello everyone! I published a blog post today that shows how to use AppAuth-JS with PKCE. https://developer.okta.com/blog/2018/09/17/desktop-app-electron-authentication

I use Okta as the IdP in this example, but it should work with anyone that supports PKCE. Thanks to everyone who posted code in this thread, it helped a lot!

tikurahul commented 6 years ago

The article looks great. Also support for PKCE is coming out of the box very soon. 😀

tikurahul commented 6 years ago

OOTB support for PKCE is here. (https://github.com/openid/AppAuth-JS/pull/79). Marking this as fixed.

aberasarte commented 6 years ago

This is great! thanks @tikurahul

tikurahul commented 6 years ago

For PKCE the interesting bits are:

This is called by the AuthorizationRequestHandler implementations when toJson() or setupCodeVerifier() is called on the AuthorizationRequest. Notice both return Promise's.


Full end to end examples are at: