Ory Kratos is a user authentication and management system. React+AntD: React+AntD: AntD: React+AntD
- Code: github.com/ikuokuo/sta…
Understand Kratos
Get the code
git clone- b v0.7.0 - alpha. 1 -- -- the depth 1 https://github.com/ory/kratos.gitCopy the code
Look at the API
Go – swagger view:
cd kratos
swagger serve -F=swagger ./spec/swagger.json
Copy the code
Run the service
Docker – compose operation:
cd kratos
docker-compose -f quickstart.yml -f quickstart-postgres.yml -f quickstart-standalone.yml up --build --force-recreate
# If you have SELinux, run: -f quickstart-selinux.yml
Copy the code
To run the official Quickstart example, can visit http://127.0.0.1:4455/dashboard experience.
Look at the DB
PgAdmin open (see Quickstart -postgres.yml for DB information) :
See table:
Check the configuration
cd kratos
cat contrib/quickstart/kratos/email-password/kratos.yml
Copy the code
Setting environment variables can override. Expressed as a _ hierarchy, such as SELFSERVICE_FLOWS_SETTINGS_UI_URL = < value > cover selfservice flows. Settings. Ui_url.
The Self – Service process
- Registration
- Login
- Logout
- User Settings
- Account Recovery
- Address Verification
- User-Facing Error
- 2FA / MFA
Browser flow
Client flow
Hands-on configuration: Kratos service
- Ory Kratos
- Public API (port 4433)
- Admin API (port 4434)
- Postgres DB (port 5432)
- Browser Return URL (port 3000)
- MailSlurper: a development SMTP server
- Server UI (port 4436)
The configuration file
- Ory – kratos/config/kratos. Yml: the configuration file
- Ory – kratos/config/identity. Schema. Json: certification json mode
Startup file
- Ory-kratos /start.yml: Docker Compose file
Run the service
cd ory-kratos
docker-compose -f start.yml up --build --force-recreate
Copy the code
If you want to run the official self-service UI example, then:
docker-compose -f start.yml -f start-ui-node.yml up --build --force-recreate
Copy the code
After that, visit http://127.0.0.1:3000/ to experience. When you Register new Account/Reset Password, you can access the virtual SMTP service http://127.0.0.1:4436 to receive emails.
Hands-on implementation: browser flow
React + Ant Design
Creating a React Application
yarn create react-app my-web --template typescript
cd my-web
yarn start
Copy the code
Visit http://localhost:3000/ to see the React welcome page.
The introduction of AntD
yarn add antd
Copy the code
Modify SRC/app.tsx to introduce ANTD component:
import React, { Component } from 'react'
import { Button } from 'antd';
import logo from './logo.svg';
import './App.css';
class App extends Component {
render() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<Button type="primary">Button</Button>
</header>
</div>); }}export default App;
Copy the code
Modify SRC/app.css to introduce antD style:
@import '~antd/dist/antd.css';
Copy the code
The ANTD blue button component is visible.
The introduction of Sass
yarn add node-sass
Copy the code
Change the suffix CSS to SCSS and import from TSX.
The introduction of the Router
yarn add react-router-dom @types/react-router-dom
Copy the code
Implement the following UI in the Pages directory:
SRC/Pages Feature Route ├─ Dashboard.tsx home /, / Dashboard ├── Error.tsx Error/Error ├── Login. TSX Login ├─ auth/ Login ├─ recovery.tsx /auth/ Registration ├─ settings.tsx set/Settings ├─ verificationCopy the code
The introduction of the SDK
Yarn add @ ory/[email protected]Copy the code
registered
APIs:
GET
/self-service/registration/browser
: Initializes the registration processGET
/self-service/registration/flows
: Gets the registration processPOST
/self-service/registration
: Submit the registration process
Processing flow after page loading:
componentDidMount() {
// Get the flow id parameter
const flowId = utils.parseUrlQuery("flow".this.props.location) as string;
// No flow ID, initialize the registration process
if(! flowId || ! utils.isString(flowId)) {console.log("No flow ID found in URL, initializing registration flow.");
utils.redirectToSelfService("/self-service/registration/browser");
return;
}
// Obtain the registration process information according to the flow ID
authPublicApi
.getSelfServiceRegistrationFlow(flowId, undefined, {
withCredentials: true,
})
.then((res: AxiosResponse<SelfServiceRegistrationFlow>) = > {
if (utils.assertResponse(res)) {
utils.redirectToSelfService("/self-service/registration/browser");
return;
}
this.setState({ flowId: flowId, flow: res.data });
})
.catch(utils.redirectOnError);
}
Copy the code
The flow information this.state.flow is as follows:
{
"id": "74c643a1-f302-45c9-a760-1ad7b1157e1c"."type": "browser"."expires_at": "The 2021-07-20 T05:22:30. 958717 z"."issued_at": "The 2021-07-20 T05:12:30. 958717 z"."request_url": "http://127.0.0.1:4433/self-service/registration/browser"."ui": {
"action": "Http://127.0.0.1:4433/self-service/registration? flow=74c643a1-f302-45c9-a760-1ad7b1157e1c"."method": "POST"."nodes": [{
"type": "input"."group": "default"."attributes": {
"name": "csrf_token"."type": "hidden"."value": "QQyUDHa4KJ3M6mowHHN4pboN4iaUOZL+4gYVtKYRWzSdWjSNcW5dG/SNzocyqqqAtV48KzQVMIC6X+Pv3tNPNw=="."required": true."disabled": false
},
"messages": []."meta": {}}, {"type": "input"."group": "password"."attributes": {
"name": "traits.email"."type": "email"."disabled": false
},
"messages": []."meta": {
"label": {
"id": 1070002,
"text": "E-Mail"."type": "info"}}}, {... }}}]Copy the code
After that, create a form based on the process information:
<Card title="Register new account" bordered={false} > {/* Process message display */}
{this.state.flow.ui.messages &&
this.state.flow.ui.messages.map((m: UiText, index) = > (
<Alert
key={index}
message={m.text}
type={m.type as AlertProps["type"]}
style={{ marginBottom: 16 }}
showIcon
/>
))}
{/* Process form creation */}
<Form
name="register"
ref={this.formRef}
encType="application/x-www-form-urlencoded"
action={this.state.flow.ui.action}
method={this.state.flow.ui.method}
onFinish={onFinish}
>
{this.state.flow.ui.nodes.map((node, index) = > {
returnReact.cloneElement(ui.toUiNodeAntd(node)! , {key: index,
});
})}
</Form>
</Card>
Copy the code
The form onFinish handles the submission:
const onFinish = (values: any) = > {
// AntD Form does not submit the original HTML Form
// - Cannot submit the find form directly, the value has been cleared
// - Create from commit, and AntD from have no effect on each other
ui.submitViaForm(this.state.flow! .ui, values);// Alternatively, file with '/self-service/registration/ API'
// this.submitViaApi(values);
};
Copy the code
The login
GET
/self-service/login/browser
: Initializes the login processGET
/self-service/login/flows
: Obtains the login processPOST
/self-service/login
: Submit the login process
Same as the registration process.
After login, you can obtain the authorization information through WHOami:
GET
/sessions/whoami
: Obtain authorization information
authPublicApi
.toSession(undefined.undefined, {
withCredentials: true,
})
.then((res: AxiosResponse<Session>) = > {
if (utils.assertResponse(res)) {
utils.redirectToSelfService("/self-service/login/browser");
return;
}
this.setState({ session: res.data });
})
.catch((err: AxiosError) = > utils.redirectOnError(err, "/auth/login"));
Copy the code
The Dashboard page shows the authorization information:
validation
GET
/self-service/verification/browser
: Initializes the verification processGET
/self-service/verification/flows
: Obtain the verification processPOST
/self-service/verification
: Submit the verification process
Same as the registration process.
restore
GET
/self-service/recovery/browser
: Initializes the recovery processGET
/self-service/recovery/flows
: Obtain the recovery processPOST
/self-service/recovery
: Submit the recovery process
Same as the registration process.
Set up the
GET
/self-service/settings/browser
: Initial configuration processGET
/self-service/settings/flows
: Obtain the configuration processPOST
/self-service/settings
: The configuration process is complete
Same as the registration process.
However, it should be noted that when creating forms based on process information, please distinguish groups and build multiple forms:
const nodesGroup: Record<
string, { title? :string; nodes? :Array<UiNode>;
}
> = {
default: {},
profile: { title: "Profile" },
password: { title: "Password" },
oidc: { title: "Social Sign In"}};for (const [k, v] of Object.entries(nodesGroup)) {
nodesGroup[k] = {
title: v.title,
nodes: ui.onlyNodes(this.state.flow! .ui.nodes, k), }; }Copy the code
<Card title="Settings" bordered={false} > {this.state.flow.ui.messages &&
this.state.flow.ui.messages.map((m: UiText, index) = > (
<Alert
key={index}
message={m.text}
type={m.type as AlertProps["type"]}
style={{ marginBottom: 16 }}
showIcon
/>
))}
{/* Split Form by group here. Otherwise, one AntD Form method conflicts. */}
{Object.entries(nodesGroup)
.filter(([k, v]) = >k ! = ="default"&& v && v.nodes! .length >0)
.map(([k, v], index) = > (
<Form
key={index}
name={k}
encType="application/x-www-form-urlencoded"
action={this.state.flow! .ui.action}
method={this.state.flow! .ui.method}
onFinish={onFinish}
>
<Form.Item>
<div>{v.title}</div>
</Form.Item>{v .nodes! .concat(nodesGroup["default"].nodes!) .map((node, index) => { return React.cloneElement(ui.toUiNodeAntd(node)! , { key: index, }); })}</Form>
))}
</Card>
Copy the code
logout
GET
/self-service/logout/browser
: Creates the logout URLPOST
/self-service/logout
: The logout process is complete
Create a logout URL after the page loads,
authPublicApi
.createSelfServiceLogoutFlowUrlForBrowsers(undefined, {
withCredentials: true,
})
.then((res: AxiosResponse<SelfServiceLogoutUrl>) = > {
this.setState({ logoutUrl: res.data.logout_url });
})
.catch((err) = > {
// console.log(err);
});
Copy the code
After that, the page adds a logout button:
{this.state.logoutUrl && (
<Button
type="link"
shape="circle"
href={this.state.logoutUrl}
icon={<LogoutOutlined />} / >
)}
Copy the code
reference
- ORY Kratos ExpressJS Self-Service UI Reference
- Kratos React Example
GoCoding personal practice experience sharing, please pay attention to the public account!