Skip to content

Commit

Permalink
react sdk
Browse files Browse the repository at this point in the history
  • Loading branch information
peintnermax committed Jan 24, 2024
1 parent f0a0334 commit e7b6ecc
Show file tree
Hide file tree
Showing 26 changed files with 562 additions and 23 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,5 @@
npm-debug.log*
yarn-debug.log*
yarn-error.log*

lib/node_modules
4 changes: 4 additions & 0 deletions lib/.releaserc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"$schema": "https://json.schemastore.org/semantic-release.json",
"branches": ["main"]
}
40 changes: 40 additions & 0 deletions lib/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# ZITADEL React SDK

Authenticate your [ZITADEL](https://zitadel.com) users within your React applications.

![NPM Version](https://img.shields.io/npm/v/@zitadel/react)
![NPM License](https://img.shields.io/npm/l/@zitadel/react)
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](https://makeapullrequest.com)

## Getting Started

- Check out the docs on how to [integrate ZITADEL into your existing React application](https://zitadel.com/docs/examples/login/react).
- Create a new React application with ZITADEL integration from scratch by following the example at [ZITADEL React example application](https://github.com/zitadel/zitadel-react/blob/main/README.md).

## Features

The NPM package [@zitadel/react](https://www.npmjs.com/package/@zitadel/react) wraps the NPM package [oidc-client-ts](https://github.com/authts/oidc-client-ts).
All [oidc-client-ts](https://github.com/authts/oidc-client-ts) features are available and the whole configuration can be overridden.

The following features are added to [oidc-client-ts](https://github.com/authts/oidc-client-ts)

- [@zitadel/react](https://www.npmjs.com/package/@zitadel/react) defaults as much configuration as possible.
- [@zitadel/react](https://www.npmjs.com/package/@zitadel/react) provides a simple way to check for user roles.

The following is an example for a minimal OIDC configuration:

```typescript
const zitadelAuth = createZitadelAuth({
issuer: `${myZITADELInstancesOrigin}`,
client_id: `${myApplicationsClientID}`,
project_resource_id: `${myApplicationsProjectResourceID}`,
});
```

The following defaults apply:

- The OIDC Code Flow with PKCE is used for authentication at ZITADEL.
- ZITADELs user info endpoint is called to enrich the user profile.
- The access token is refreshed automatically by default before it expires.
- If you specify a _project_resource_id_, the scopes for retrieving the users roles from the user info endpoint are added automatically.
You can conveniently use `zitadelAuth.hasRole("someRoleKey")`.
1 change: 1 addition & 0 deletions lib/dist/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { createZITADELAuth, ZitadelConfig } from "./zitadelAuth";
1 change: 1 addition & 0 deletions lib/dist/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { createZITADELAuth } from "./zitadelAuth";
1 change: 1 addition & 0 deletions lib/dist/lib/src/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { createZITADELAuth, ZitadelConfig } from "./zitadelAuth";
1 change: 1 addition & 0 deletions lib/dist/lib/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { createZITADELAuth } from "./zitadelAuth";
13 changes: 13 additions & 0 deletions lib/dist/lib/src/zitadelAuth.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { UserManager } from "oidc-client-ts";
export interface ZitadelConfig {
client_id: string;
issuer: string;
project_resource_id?: string;
}
interface ZitadelAuth {
authorize(): Promise<void>;
clearAuth(): Promise<void>;
userManager: UserManager;
}
export declare function createZITADELAuth(zitadelConfig: ZitadelConfig): ZitadelAuth;
export {};
33 changes: 33 additions & 0 deletions lib/dist/lib/src/zitadelAuth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { UserManager, WebStorageStateStore, } from "oidc-client-ts";
export function createZITADELAuth(zitadelConfig) {
const authConfig = {
authority: `${zitadelConfig.issuer}`, //Replace with your issuer URL
client_id: `${zitadelConfig.client_id}`, //Replace with your client id
redirect_uri: "http://localhost:3000/callback",
response_type: "code",
scope: `openid profile email ${zitadelConfig.project_resource_id
? `urn:zitadel:iam:org:project:id:${zitadelConfig.project_resource_id}:aud urn:zitadel:iam:org:projects:roles`
: ""}`,
post_logout_redirect_uri: "http://localhost:3000/",
// userinfo_endpoint:
// "https://instance-some_text.zitadel.cloud/oidc/v1/userinfo",
response_mode: "query",
// code_challenge_method: "S256",
};
const userManager = new UserManager({
userStore: new WebStorageStateStore({ store: window.localStorage }),
...authConfig,
});
const authorize = () => {
return userManager.signinRedirect();
};
const clearAuth = () => {
return userManager.signoutRedirect();
};
const oidc = {
authorize,
clearAuth,
userManager,
};
return oidc;
}
11 changes: 11 additions & 0 deletions lib/dist/src/components/Callback.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/// <reference types="react" />
type Props = {
authenticated: boolean;
setAuth: (authenticated: boolean) => void;
userManager: any;
userInfo: any;
setUserInfo: any;
handleLogout: any;
};
declare const Callback: ({ authenticated, setAuth, userManager, userInfo, setUserInfo, handleLogout, }: Props) => import("react").JSX.Element;
export default Callback;
48 changes: 48 additions & 0 deletions lib/dist/src/components/Callback.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { useEffect } from "react";
const Callback = ({ authenticated, setAuth, userManager, userInfo, setUserInfo, handleLogout, }) => {
useEffect(() => {
if (authenticated === false) {
userManager
.signinRedirectCallback()
.then((user) => {
if (user) {
setAuth(true);
const access_token = user.access_token;
// Make a request to the user info endpoint using the access token
// fetch(authConfig.userinfo_endpoint, {
// headers: {
// 'Authorization': `Bearer ${access_token}`
// }
// })
// .then(response => response.json())
// .then(userInfo => {
// setUserInfo(userInfo);
// });
}
else {
setAuth(false);
}
})
.catch((error) => {
setAuth(false);
});
}
}, [authenticated, userManager, setAuth]);
console.log(authenticated, userInfo);
if (authenticated === true && userInfo) {
return (<div>
<h1>Welcome, {userInfo.name}!</h1>
<h2>Your ZITADEL Profile Information</h2>
<h3>Name: {userInfo.name}</h3>
<h3>Email: {userInfo.email}</h3>
<h3>Email Verified: {userInfo.email_verified ? "Yes" : "No"}</h3>
<h3>Locale: {userInfo.locale}</h3>

<button onClick={handleLogout}>Log out</button>
</div>);
}
else {
return <div>Loading...</div>;
}
};
export default Callback;
7 changes: 7 additions & 0 deletions lib/dist/src/components/Login.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/// <reference types="react" />
type Props = {
handleLogin: () => void;
authenticated: boolean;
};
declare const Login: ({ authenticated, handleLogin }: Props) => import("react").JSX.Element;
export default Login;
17 changes: 17 additions & 0 deletions lib/dist/src/components/Login.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Navigate } from "react-router-dom";
const Login = ({ authenticated, handleLogin }) => {
return (<div>
{authenticated === null && <div>Loading...</div>}
{authenticated === false && (<div>
<h1>Welcome!</h1>
<button onClick={() => {
// Perform the authorization request, including the code challenge
handleLogin();
}}>
Please log in.
</button>
</div>)}
{authenticated && <Navigate to="/callback"/>}
</div>);
};
export default Login;
13 changes: 13 additions & 0 deletions lib/dist/zitadelAuth.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { UserManager } from "oidc-client-ts";
export interface ZitadelConfig {
client_id: string;
issuer: string;
project_resource_id?: string;
}
interface ZitadelAuth {
authorize(): Promise<void>;
clearAuth(): Promise<void>;
userManager: UserManager;
}
export declare function createZITADELAuth(zitadelConfig: ZitadelConfig): ZitadelAuth;
export {};
33 changes: 33 additions & 0 deletions lib/dist/zitadelAuth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { UserManager, WebStorageStateStore, } from "oidc-client-ts";
export function createZITADELAuth(zitadelConfig) {
const authConfig = {
authority: `${zitadelConfig.issuer}`, //Replace with your issuer URL
client_id: `${zitadelConfig.client_id}`, //Replace with your client id
redirect_uri: "http://localhost:3000/callback",
response_type: "code",
scope: `openid profile email ${zitadelConfig.project_resource_id
? `urn:zitadel:iam:org:project:id:${zitadelConfig.project_resource_id}:aud urn:zitadel:iam:org:projects:roles`
: ""}`,
post_logout_redirect_uri: "http://localhost:3000/",
// userinfo_endpoint:
// "https://instance-some_text.zitadel.cloud/oidc/v1/userinfo",
response_mode: "query",
// code_challenge_method: "S256",
};
const userManager = new UserManager({
userStore: new WebStorageStateStore({ store: window.localStorage }),
...authConfig,
});
const authorize = () => {
return userManager.signinRedirect();
};
const clearAuth = () => {
return userManager.signoutRedirect();
};
const oidc = {
authorize,
clearAuth,
userManager,
};
return oidc;
}
36 changes: 36 additions & 0 deletions lib/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"name": "@zitadel/react",
"version": "0.0.0-development",
"description": "Wraps oidc-client-ts to provide a simple interface for ZITADEL",
"homepage": "https://zitadel.com/docs/examples/login/react",
"bugs": {
"url": "https://github.com/zitadel/zitadel-react/issues"
},
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"publishConfig": {
"access": "public"
},
"scripts": {
"build": "tsc"
},
"keywords": [
"ZITADEL",
"React",
"authN",
"authZ",
"OIDC",
"IAM"
],
"files": [
"/dist"
],
"author": "Max Peintner, <[email protected]>",
"license": "MIT",
"dependencies": {
"oidc-client-ts": "^2.4.0"
},
"devDependencies": {
"typescript": "^5.3.3"
}
}
1 change: 1 addition & 0 deletions lib/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { createZITADELAuth, ZitadelConfig } from "./zitadelAuth";
58 changes: 58 additions & 0 deletions lib/src/zitadelAuth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import {
UserManagerSettings,
Logger,
UserManager,
WebStorageStateStore,
} from "oidc-client-ts";

export interface ZitadelConfig {
client_id: string;
issuer: string;
project_resource_id?: string;
}

interface ZitadelAuth {
authorize(): Promise<void>;
clearAuth(): Promise<void>;
userManager: UserManager;
}

export function createZITADELAuth(zitadelConfig: ZitadelConfig): ZitadelAuth {
const authConfig: UserManagerSettings = {
authority: `${zitadelConfig.issuer}`, //Replace with your issuer URL
client_id: `${zitadelConfig.client_id}`, //Replace with your client id
redirect_uri: "http://localhost:3000/callback",
response_type: "code",
scope: `openid profile email ${
zitadelConfig.project_resource_id
? `urn:zitadel:iam:org:project:id:${zitadelConfig.project_resource_id}:aud urn:zitadel:iam:org:projects:roles`
: ""
}`,
post_logout_redirect_uri: "http://localhost:3000/",
// userinfo_endpoint:
// "https://instance-some_text.zitadel.cloud/oidc/v1/userinfo",
response_mode: "query",
// code_challenge_method: "S256",
};

const userManager = new UserManager({
userStore: new WebStorageStateStore({ store: window.localStorage }),
...authConfig,
});

const authorize = () => {
return userManager.signinRedirect();
};

const clearAuth = () => {
return userManager.signoutRedirect();
};

const oidc = {
authorize,
clearAuth,
userManager,
};

return oidc;
}
11 changes: 11 additions & 0 deletions lib/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"compilerOptions": {
"module": "ESNext",
"target": "es2019",
"declaration": true,
"outDir": "./dist",
"moduleResolution": "node",
"skipLibCheck": true
},
"include": ["src/**/*"]
}
26 changes: 26 additions & 0 deletions lib/yarn.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1


crypto-js@^4.2.0:
version "4.2.0"
resolved "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz"
integrity sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==

jwt-decode@^3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/jwt-decode/-/jwt-decode-3.1.2.tgz#3fb319f3675a2df0c2895c8f5e9fa4b67b04ed59"
integrity sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A==

oidc-client-ts@^2.4.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/oidc-client-ts/-/oidc-client-ts-2.4.0.tgz#764c8a33de542026e2798de9849ce8049047d7e5"
integrity sha512-WijhkTrlXK2VvgGoakWJiBdfIsVGz6CFzgjNNqZU1hPKV2kyeEaJgLs7RwuiSp2WhLfWBQuLvr2SxVlZnk3N1w==
dependencies:
crypto-js "^4.2.0"
jwt-decode "^3.1.2"

typescript@^5.3.3:
version "5.3.3"
resolved "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz"
integrity sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@
"@types/node": "^16.7.13",
"@types/react": "^18.0.0",
"@types/react-dom": "^18.0.0",
"@zitadel/react": "link:./lib",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.21.3",
"react-scripts": "5.0.1",
"typescript": "^4.4.2",
"web-vitals": "^2.1.0"
Expand Down
Loading

0 comments on commit e7b6ecc

Please sign in to comment.