Skip to content

Commit

Permalink
Support implicit flow
Browse files Browse the repository at this point in the history
  • Loading branch information
WietseWind committed Nov 21, 2022
1 parent 34d634a commit 15974fb
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 51 deletions.
39 changes: 30 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,38 @@ Demo? https://oauth2-pkce-demo.xumm.dev
NPM:
[https://www.npmjs.com/package/xumm-oauth2-pkce](https://www.npmjs.com/package/xumm-oauth2-pkce)

## Sample:
## Constructor

#### Event based sample
```
new XummPkce('api-key-uuidv4', { options })
```

#### Options

```
interface XummPkceOptions {
redirectUrl: string; // Defaults to `document.location.href`, e.g. to add state params.
rememberJwt: boolean; // Defaults to `true`
storage: Storage; // Defaults to window.localStorage
implicit: boolean; // Defaults to `false`, `true` allows x-browser sign in, but it less secure
}
```

## Samples:

#### Event based

Please note: please use the Event based sample (above) if possible: this is more compatible with future
releases than the promise-based (await/async) method as displayed below.

### See [this example (source code)](https://github.com/XRPL-Labs/XummPkce/blob/main/sample/jsmodule.html) :)

#### Events (emitted)

- `success` = User signed in successfully, `sdk.state()` returns `.me` and `.sdk` objects
- `retrieved` = Retrieved existing session after e.g. browser refresh or mobile redirect, `sdk.state()` returns `.me` and `.sdk` objects
- `error` = Error, expected (e.g. user cancelled) or unexpected (...), returns argument `error` with an `Error()` object, `sdk.state()` returns null

##### See [this example](https://github.com/XRPL-Labs/XummPkce/blob/main/sample/jsmodule.html) :)

#### Promise based sample

Expand Down Expand Up @@ -41,12 +68,6 @@ document.getElementById("somebutton").onclick = () => {
};
```

#### Events (emitted)

- `success` = User signed in successfully, `sdk.state()` returns `.me` and `.sdk` objects
- `retrieved` = Retrieved existing session after e.g. browser refresh or mobile redirect, `sdk.state()` returns `.me` and `.sdk` objects
- `error` = Error, expected (e.g. user cancelled) or unexpected (...), returns argument `error` with an `Error()` object, `sdk.state()` returns null


### CDN (browser):

Expand Down
34 changes: 17 additions & 17 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
],
"dependencies": {
"debug": "^4.1.1",
"js-pkce": "^1.2.1",
"xumm-js-pkce": "^1.0.0",
"xumm-sdk": "^1.5.0"
},
"devDependencies": {
Expand Down
7 changes: 5 additions & 2 deletions sample/jsmodule.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,12 @@ <h2 id="sub">... (please sign in)</h2>
<button id="auth">Auth</button>
<button id="logout">Logout</button>
<script type="module">
import 'https://xumm.app/assets/cdn/xumm-oauth2-pkce.min.js?v=2.4.0'
import 'https://xumm.app/assets/cdn/xumm-oauth2-pkce.min.js?v=2.5.0'

const xumm = new XummPkce('47d328db-0b34-4451-a258-393480c9b4cd')
const xumm = new XummPkce('47d328db-0b34-4451-a258-393480c9b4cd', {
implicit: true, // Implicit: allows to e.g. move from social browser to stock browser
redirectUrl: document.location.href + '?custom_state=test'
})

document.getElementById('auth').onclick = () => {
xumm.authorize().catch(e => console.log('e', e))
Expand Down
63 changes: 41 additions & 22 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// import { debug as Debug } from "debug";
import { EventEmitter } from "events";
import { XummSdkJwt } from "xumm-sdk";
import PKCE from "js-pkce";
import PKCE from "xumm-js-pkce";

// localStorage.debug = "xummpkce*";

Expand All @@ -19,6 +19,7 @@ interface XummPkceOptions {
redirectUrl: string;
rememberJwt: boolean;
storage: Storage;
implicit: boolean;
}

interface ResolvedFlow {
Expand Down Expand Up @@ -82,6 +83,7 @@ export class XummPkceThread extends EventEmitter {
redirectUrl: document.location.href,
rememberJwt: true,
storage: localStorage,
implicit: false,
};

/**
Expand All @@ -97,23 +99,30 @@ export class XummPkceThread extends EventEmitter {
this.options.redirectUrl = optionsOrRedirectUrl.redirectUrl;
}
if (typeof optionsOrRedirectUrl.rememberJwt === "boolean") {
this.options.rememberJwt = optionsOrRedirectUrl.rememberJwt;
}
if (typeof optionsOrRedirectUrl.storage === "object") {
this.options.storage = optionsOrRedirectUrl.storage;
}
if (typeof optionsOrRedirectUrl.implicit === "boolean") {
this.options.implicit = optionsOrRedirectUrl.implicit;
}
}

/**
* Construct
*/
this.pkce = new PKCE({
const pkceOptions = {
client_id: xummApiKey,
redirect_uri: this.options.redirectUrl,
authorization_endpoint: "https://oauth2.xumm.app/auth",
token_endpoint: "https://oauth2.xumm.app/token",
requested_scopes: "XummPkce",
storage: this.options.storage,
});
implicit: this.options.implicit,
};
// console.log(JSON.stringify(pkceOptions, null, 2));
this.pkce = new PKCE(pkceOptions);

/**
* Check if there is already a valid JWT to be used
Expand Down Expand Up @@ -281,7 +290,11 @@ export class XummPkceThread extends EventEmitter {
);

const params = new URLSearchParams(document?.location?.search || "");
if (params.get("authorization_code") || params.get("error_description")) {
if (
params.get("authorization_code") ||
params.get("access_token") ||
params.get("error_description")
) {
this.mobileRedirectFlow = true;
this.urlParams = params;

Expand All @@ -307,7 +320,8 @@ export class XummPkceThread extends EventEmitter {

const messageEventData = {
data: JSON.stringify(
this.urlParams.get("authorization_code")
this.urlParams.get("authorization_code") ||
this.urlParams.get("access_token")
? {
source: "xumm_sign_request_resolved",
options: {
Expand Down Expand Up @@ -357,23 +371,27 @@ export class XummPkceThread extends EventEmitter {
this.resolved = false;

const clearUrlParams = () => {
if (this.urlParams && this.mobileRedirectFlow) {
const newUrlParams = new URLSearchParams(
document?.location?.search || ""
);
newUrlParams.delete("authorization_code");
newUrlParams.delete("code");
newUrlParams.delete("state");
const newSearchParamsString = newUrlParams.toString();

history.replaceState(
{},
"",
document.location.href.split("?")[0] +
(newSearchParamsString !== "" ? "?" : "") +
newSearchParamsString
);
}
const newUrlParams = new URLSearchParams(
document?.location?.search || ""
);
// PKCE
newUrlParams.delete("authorization_code");
newUrlParams.delete("code");
newUrlParams.delete("scope");
newUrlParams.delete("state");
// Implicit
newUrlParams.delete("access_token");
newUrlParams.delete("refresh_token");
newUrlParams.delete("token_type");
newUrlParams.delete("expires_in");
const newSearchParamsString = newUrlParams.toString();

const url =
document.location.href.split("?")[0] +
(newSearchParamsString !== "" ? "?" : "") +
newSearchParamsString;

(window as any).history.replaceState({ path: url }, "", url);
};

clearUrlParams();
Expand Down Expand Up @@ -419,6 +437,7 @@ export class XummPkceThread extends EventEmitter {
this.resolvedSuccessfully = undefined;
this.autoResolvedFlow = undefined;
this.options.storage?.removeItem("XummPkceJwt");
this.mobileRedirectFlow = false;
} catch (e) {
// Nothing to do
}
Expand Down

0 comments on commit 15974fb

Please sign in to comment.