oidc-spa 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +205 -0
- package/index.d.ts +4 -0
- package/index.js +11 -0
- package/index.js.map +1 -0
- package/oidc.d.ts +37 -0
- package/oidc.js +440 -0
- package/oidc.js.map +1 -0
- package/package.json +83 -0
- package/react.d.ts +11 -0
- package/react.js +58 -0
- package/react.js.map +1 -0
- package/src/index.ts +4 -0
- package/src/oidc.ts +398 -0
- package/src/react.tsx +33 -0
- package/src/tools/Deferred.ts +39 -0
- package/src/tools/fnv1aHashToHex.ts +8 -0
- package/src/tools/readExpirationTimeInJwt.ts +17 -0
- package/src/tools/urlSearchParams.ts +86 -0
- package/tools/Deferred.d.ts +14 -0
- package/tools/Deferred.js +50 -0
- package/tools/Deferred.js.map +1 -0
- package/tools/fnv1aHashToHex.d.ts +1 -0
- package/tools/fnv1aHashToHex.js +13 -0
- package/tools/fnv1aHashToHex.js.map +1 -0
- package/tools/readExpirationTimeInJwt.d.ts +1 -0
- package/tools/readExpirationTimeInJwt.js +22 -0
- package/tools/readExpirationTimeInJwt.js.map +1 -0
- package/tools/urlSearchParams.d.ts +26 -0
- package/tools/urlSearchParams.js +84 -0
- package/tools/urlSearchParams.js.map +1 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2020 GitHub user u/garronej
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://user-images.githubusercontent.com/6702424/80216211-00ef5280-863e-11ea-81de-59f3a3d4b8e4.png">
|
|
3
|
+
</p>
|
|
4
|
+
<p align="center">
|
|
5
|
+
<i>Openidconnect client for Single Page Applications</i>
|
|
6
|
+
<br>
|
|
7
|
+
<br>
|
|
8
|
+
<a href="https://github.com/garronej/oidc-spa/actions">
|
|
9
|
+
<img src="https://github.com/garronej/oidc-spa/workflows/ci/badge.svg?branch=main">
|
|
10
|
+
</a>
|
|
11
|
+
<a href="https://bundlephobia.com/package/oidc-spa">
|
|
12
|
+
<img src="https://img.shields.io/bundlephobia/minzip/oidc-spa">
|
|
13
|
+
</a>
|
|
14
|
+
<a href="https://www.npmjs.com/package/oidc-spa">
|
|
15
|
+
<img src="https://img.shields.io/npm/dw/oidc-spa">
|
|
16
|
+
</a>
|
|
17
|
+
<a href="https://github.com/garronej/oidc-spa/blob/main/LICENSE">
|
|
18
|
+
<img src="https://img.shields.io/npm/l/oidc-spa">
|
|
19
|
+
</a>
|
|
20
|
+
</p>
|
|
21
|
+
<p align="center">
|
|
22
|
+
<a href="https://github.com/garronej/oidc-spa">Home</a>
|
|
23
|
+
-
|
|
24
|
+
<a href="https://github.com/garronej/oidc-spa">Documentation</a>
|
|
25
|
+
</p>
|
|
26
|
+
|
|
27
|
+
An OIDC client for Single Page Applications that comes with an optional adapter for React.
|
|
28
|
+
Very minimal API surface, you don't need to know the in and out of oidc or OAuth to use this.
|
|
29
|
+
|
|
30
|
+
Why not oidc-client-ts ?
|
|
31
|
+
|
|
32
|
+
- `oidc-client-ts` more a toolkit than a ready to use adapter. This lib use it internally but abstract away most of it's complexity.
|
|
33
|
+
- It's used under the hood by this lib but it's hard to setup directly in a SPA setup especially the silent SSO.
|
|
34
|
+
- It's more resilient do misconfigured server.
|
|
35
|
+
- It restrict what params can be passed on the url when redirecting to the login page.
|
|
36
|
+
|
|
37
|
+
Why not react-oidc-context?
|
|
38
|
+
|
|
39
|
+
- There's no overlap between OIDC and React. Not everything should be though as a React problem. Oidc and React are
|
|
40
|
+
completely different problem space they have no business being intertwined.
|
|
41
|
+
This library provide an optional React adapter for convenience in the spirit of being truly ready to use but don't
|
|
42
|
+
get mistaken, it's trivial you could as well do without it.
|
|
43
|
+
|
|
44
|
+
# Install / Import
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
$ yarn add oidc-spa
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Create a `silent-sso.html` file and put it in your public directory.
|
|
51
|
+
|
|
52
|
+
```html
|
|
53
|
+
<html>
|
|
54
|
+
<body>
|
|
55
|
+
<script>
|
|
56
|
+
parent.postMessage(location.href, location.origin);
|
|
57
|
+
</script>
|
|
58
|
+
</body>
|
|
59
|
+
</html>
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
# Usage
|
|
63
|
+
|
|
64
|
+
## Isolated from the UI library
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
import { createOidc, decodeJwt } from "oidc-spa";
|
|
68
|
+
|
|
69
|
+
(async () => {
|
|
70
|
+
const oidc = await createOidc({
|
|
71
|
+
issuerUri: "https://auth.your-domain.net/auth/realms/myrealm",
|
|
72
|
+
clientId: "myclient",
|
|
73
|
+
// Optional, you can modify the url before redirection to the identity server
|
|
74
|
+
transformUrlBeforeRedirect: url => `${url}&ui_locales=fr`
|
|
75
|
+
/** Optional: Provide only if your app is not hosted at the origin */
|
|
76
|
+
//silentSsoUrl: `${window.location.origin}/foo/bar/baz/silent-sso.html`,
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
if (oidc.isUserLoggedIn) {
|
|
80
|
+
// This return a promise that never resolve. Your user will be redirected to the identity server.
|
|
81
|
+
// If you are calling login because the user clicked
|
|
82
|
+
// on a 'login' button you should set doesCurrentHrefRequiresAuth to false.
|
|
83
|
+
// When you are calling login because your user navigated to a path that require authentication
|
|
84
|
+
// you should set doesCurrentHrefRequiresAuth to true
|
|
85
|
+
oidc.login({ doesCurrentHrefRequiresAuth: false });
|
|
86
|
+
} else {
|
|
87
|
+
const {
|
|
88
|
+
// The accessToken is what you'll use a a Bearer token to authenticate to your APIs
|
|
89
|
+
accessToken,
|
|
90
|
+
// You can parse the idToken as a JWT to get some information about the user.
|
|
91
|
+
idToken
|
|
92
|
+
} = oidc.getTokens();
|
|
93
|
+
|
|
94
|
+
const { sub, preferred_username } = decodeJwt<{
|
|
95
|
+
// Use https://jwt.io/ to tell what's in your idToken
|
|
96
|
+
sub: string;
|
|
97
|
+
preferred_username: string;
|
|
98
|
+
}>(idToken);
|
|
99
|
+
|
|
100
|
+
// To call when the user click on logout.
|
|
101
|
+
oidc.logout();
|
|
102
|
+
}
|
|
103
|
+
})();
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Use via React Adapter
|
|
107
|
+
|
|
108
|
+
```tsx
|
|
109
|
+
import { createOidcProvider, useOidc } from "oidc-spa/react";
|
|
110
|
+
import { decodeJwt } from "oidc-spa";
|
|
111
|
+
|
|
112
|
+
const { OidcProvider } = createOidcProvider({
|
|
113
|
+
issuerUri: "https://auth.your-domain.net/auth/realms/myrealm",
|
|
114
|
+
clientId: "myclient"
|
|
115
|
+
// See above for other parameters
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
ReactDOM.render(
|
|
119
|
+
<OidcProvider
|
|
120
|
+
// Optional, it's usually so fast that a fallback is really not required.
|
|
121
|
+
fallback={<>Logging you in...</>}
|
|
122
|
+
>
|
|
123
|
+
<App />
|
|
124
|
+
</OidcProvider>,
|
|
125
|
+
document.getElementById("root")
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
function App() {
|
|
129
|
+
const { oidc } = useOidc();
|
|
130
|
+
|
|
131
|
+
if (!oidc.isUserLoggedIn) {
|
|
132
|
+
return (
|
|
133
|
+
<>
|
|
134
|
+
You're not logged in.
|
|
135
|
+
<button onClick={() => oidc.login({ doesCurrentHrefRequiresAuth: false })}>
|
|
136
|
+
Login now
|
|
137
|
+
</button>
|
|
138
|
+
</>
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const { preferred_username } = decodeJwt<{
|
|
143
|
+
preferred_username: string;
|
|
144
|
+
}>(oidc.getTokens().idToken);
|
|
145
|
+
|
|
146
|
+
return (
|
|
147
|
+
<>
|
|
148
|
+
<h1>Hello {preferred_username}</h1>
|
|
149
|
+
<button onClick={() => oidc.logout()}>Log out</button>
|
|
150
|
+
</>
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
# Setup example
|
|
156
|
+
|
|
157
|
+
- Basic setup: https://github.com/keycloakify/keycloakify-starter
|
|
158
|
+
- Fully fledged app: https://github.com/InseeFrLab/onyxia
|
|
159
|
+
|
|
160
|
+
# Contributing
|
|
161
|
+
|
|
162
|
+
## Testing your changes in an external app
|
|
163
|
+
|
|
164
|
+
You have made some changes to the code and you want to test them
|
|
165
|
+
in your app before submitting a pull request?
|
|
166
|
+
|
|
167
|
+
Assuming `you/my-app` have `oidc-spa` as a dependency.
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
cd ~/github
|
|
171
|
+
git clone https://github.com/you/my-app
|
|
172
|
+
cd my-app
|
|
173
|
+
yarn
|
|
174
|
+
|
|
175
|
+
cd ~/github
|
|
176
|
+
git clone https://github.com/garronej/oidc-spa
|
|
177
|
+
cd oidc-spa
|
|
178
|
+
yarn
|
|
179
|
+
yarn build
|
|
180
|
+
yarn link-in-app my-app
|
|
181
|
+
npx tsc -w
|
|
182
|
+
|
|
183
|
+
# Open another terminal
|
|
184
|
+
|
|
185
|
+
cd ~/github/my-app
|
|
186
|
+
rm -rf node_modules/.cache
|
|
187
|
+
yarn start # Or whatever my-app is using for starting the project
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
You don't have to use `~/github` as reference path. Just make sure `my-app` and `oidc-spa`
|
|
191
|
+
are in the same directory.
|
|
192
|
+
|
|
193
|
+
> Note for the maintainer: You might run into issues if you do not list all your singleton dependencies in
|
|
194
|
+
> `src/link-in-app.js -> singletonDependencies`. A singleton dependency is a dependency that can
|
|
195
|
+
> only be present once in an App. Singleton dependencies are usually listed as peerDependencies example `react`, `@emotion/*`.
|
|
196
|
+
|
|
197
|
+
## Releasing
|
|
198
|
+
|
|
199
|
+
For releasing a new version on GitHub and NPM you don't need to create a tag.
|
|
200
|
+
Just update the `package.json` version number and push.
|
|
201
|
+
|
|
202
|
+
For publishing a release candidate update your `package.json` with `1.3.4-rc.0` (`.1`, `.2`, ...).
|
|
203
|
+
It also work if you do it from a branch that have an open PR on main.
|
|
204
|
+
|
|
205
|
+
> Make sure your have defined the `NPM_TOKEN` repository secret or NPM publishing will fail.
|
package/index.d.ts
ADDED
package/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.decodeJwt = exports.createOidc = void 0;
|
|
7
|
+
var oidc_1 = require("./oidc");
|
|
8
|
+
Object.defineProperty(exports, "createOidc", { enumerable: true, get: function () { return oidc_1.createOidc; } });
|
|
9
|
+
var jwt_decode_1 = __importDefault(require("jwt-decode"));
|
|
10
|
+
exports.decodeJwt = jwt_decode_1.default;
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
package/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["src/index.ts"],"names":[],"mappings":";;;;;;AAAA,+BAAoC;AAA3B,kGAAA,UAAU,OAAA;AAEnB,0DAAmC;AAC1B,oBADF,oBAAS,CACE"}
|
package/oidc.d.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export declare type Oidc = Oidc.LoggedIn | Oidc.NotLoggedIn;
|
|
2
|
+
export declare namespace Oidc {
|
|
3
|
+
type Common = {
|
|
4
|
+
params: {
|
|
5
|
+
issuerUri: string;
|
|
6
|
+
clientId: string;
|
|
7
|
+
};
|
|
8
|
+
};
|
|
9
|
+
type NotLoggedIn = Common & {
|
|
10
|
+
isUserLoggedIn: false;
|
|
11
|
+
login: (params: {
|
|
12
|
+
doesCurrentHrefRequiresAuth: boolean;
|
|
13
|
+
}) => Promise<never>;
|
|
14
|
+
};
|
|
15
|
+
type LoggedIn = Common & {
|
|
16
|
+
isUserLoggedIn: true;
|
|
17
|
+
renewTokens(): Promise<void>;
|
|
18
|
+
getTokens: () => Tokens;
|
|
19
|
+
logout: (params: {
|
|
20
|
+
redirectTo: "home" | "current page";
|
|
21
|
+
}) => Promise<never>;
|
|
22
|
+
};
|
|
23
|
+
type Tokens = {
|
|
24
|
+
accessToken: string;
|
|
25
|
+
accessTokenExpirationTime: number;
|
|
26
|
+
idToken: string;
|
|
27
|
+
refreshToken: string;
|
|
28
|
+
refreshTokenExpirationTime: number;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
export declare function createOidc(params: {
|
|
32
|
+
issuerUri: string;
|
|
33
|
+
clientId: string;
|
|
34
|
+
transformUrlBeforeRedirect?: (url: string) => string;
|
|
35
|
+
/** Default `${window.location.origin}/silent-sso.html` */
|
|
36
|
+
silentSsoUrl?: string;
|
|
37
|
+
}): Promise<Oidc>;
|