Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/myazarc/elysia-oauth2-server
A customized OAuth 2.0 server builder for Elysia.js
https://github.com/myazarc/elysia-oauth2-server
Last synced: 2 months ago
JSON representation
A customized OAuth 2.0 server builder for Elysia.js
- Host: GitHub
- URL: https://github.com/myazarc/elysia-oauth2-server
- Owner: myazarc
- License: mit
- Created: 2023-12-21T21:08:39.000Z (about 1 year ago)
- Default Branch: main
- Last Pushed: 2023-12-21T22:46:02.000Z (about 1 year ago)
- Last Synced: 2024-04-24T16:59:05.142Z (8 months ago)
- Language: TypeScript
- Size: 13.7 KB
- Stars: 5
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# OAuth2 Server implementation for Elysia
An extension for quickly and easily developing an OAuth2 server with Elysia.
It has been developed based on the [express-oauth-server](https://github.com/oauthjs/express-oauth-server) module using the [@node-oauth/oauth2-server](https://github.com/node-oauth/node-oauth2-server) module.
Complete, compliant and well tested module for implementing an OAuth2 Server/Provider with [Elysia](https://elysiajs.com/introduction) in [Bun](https://bun.sh).
This is the [Elysia](https://elysiajs.com/introduction) wrapper for oauth2-server. :)
## Installation
```bash
bun add @myazarc/elysia-oauth2-server
```## Supported flows
- password
- authorization_code
- client_credentials
- refresh_tokenfor details: [https://oauth.net/2/grant-types/](https://oauth.net/2/grant-types/)
## Using
### Step 1: Choose the flow you will use and create the model.
```ts
const clients: Client[] = [
{
id: 'clientId1',
clientSecret: 'client@secret',
redirectUris: ['http://localhost:3000'],
// only those allowed
grants: [
'password',
'authorization_code',
'client_credentials',
'refresh_token',
],
},
];
const users = [{ id: '123', username: 'mira', password: '12345' }];const tokens: any[] = [];
const codes: any[] = [];const oauth2Opts: Oauth2Options = {
model: {
//#region all
getAccessToken: async (accessToken: string) => {
return tokens.find((token) => token.accessToken === accessToken);
},
getClient: async (clientId: string, clientSecret: string) => {
if (clientSecret === null) {
const client = clients.find((client) => client.id === clientId);
return client;
}const client = clients.find(
(client) =>
client.id === clientId && client.clientSecret === clientSecret,
);
return client;
},
getUser: async (username: string, password: string) => {
return users.find(
(user) => user.username === username && user.password === password,
);
},
saveToken: async (token: any, client: any, user: any) => {
const tokenData: Token = {
accessToken: token.accessToken,
accessTokenExpiresAt: token.accessTokenExpiresAt,
refreshToken: token.refreshToken,
refreshTokenExpiresAt: token.refreshTokenExpiresAt,
client: {
id: client.id,
grants: client.grants,
},
user: {
id: user.id,
},
};tokens.push(tokenData);
return tokenData;
},//#endregion
//#region authorization code
saveAuthorizationCode: async (code: any, client: any, user: any) => {
codes.push({
...code,
client: {
id: client.id,
grants: client.grants,
},
user: {
id: user.id,
},
});
return code;
},getAuthorizationCode: async (authorizationCode: string) => {
const a = codes.find(
(code) => code.authorizationCode === authorizationCode,
);
return a;
},
revokeAuthorizationCode: async (code: any) => {
const index = codes.findIndex(
(c) => c.authorizationCode === code.authorizationCode,
);
codes.splice(index, 1);
return true;
},//#endregion
//#region client credentials
getUserFromClient: async (client: any) => {
// TODO: find related user
return users[0];
},//#endregion
//#region refresh token
getRefreshToken: async (refreshToken: string) => {
return tokens.find((token) => token.refreshToken === refreshToken);
},revokeToken: async (token: any) => {
const index = tokens.findIndex(
(t) => t.accessToken === token.accessToken,
);
tokens.splice(index, 1);
return true;
},//#endregion
},
};
```### Step 2: Integration with Elysia and adding routes.
```ts
new Elysia()
.use(oauth2(oauth2Opts)) // oauth2 wrapper
.post(
// getting token
'/oauth2/token',
({ oauth2, ...payload }) => {
return oauth2.token(payload);
},
{
headers: t.Object({
// Example: "Basic " + Buffer.from("clientId1:client@secret").toString("base64"),
authorization: t.String(),
}),
body: t.Object({
username: t.Optional(t.String()),
password: t.Optional(t.String()),
grant_type: t.String({
examples: [
'password',
'refresh_token',
'client_credentials',
'authorization_code',
],
}),
code: t.Optional(t.String()),
redirect_uri: t.Optional(t.String()),
refresh_token: t.Optional(t.String()),
}),
},
)
.post(
// verifing token
'/oauth2/authenticate',
({ oauth2, ...payload }) => {
return oauth2.authenticate(payload);
},
{
headers: t.Object({
// Example: "Basic " + Buffer.from("clientId1:client@secret").toString("base64"),
authorization: t.String(),
}),
body: t.Object({
token: t.String(),
}),
},
)
.post(
// checking credentials
'/oauth2/authorize',
({ oauth2, ...payload }) => {
return oauth2.authorize(payload, {
// TODO: implement.
authenticateHandler: {
handle: (request: any) => {
return { id: '123' };
},
},
});
},
{
body: t.Object({
client_id: t.String(),
response_type: t.String({
examples: ['code', 'token'],
}),
state: t.String(),
redirect_uri: t.String(),
}),
},
)
.listen(3000);
```### Full Code
```ts
import { Elysia, t } from 'elysia';
import {
Oauth2Options,
oauth2,
Client,
Token,
} from '@myazarc/elysia-oauth2-server';
const clients: Client[] = [
{
id: 'clientId1',
clientSecret: 'client@secret',
redirectUris: ['http://localhost:3000'],
// only those allowed
grants: [
'password',
'authorization_code',
'client_credentials',
'refresh_token',
],
},
];
const users = [{ id: '123', username: 'mira', password: '12345' }];const tokens: any[] = [];
const codes: any[] = [];const oauth2Opts: Oauth2Options = {
model: {
//#region all
getAccessToken: async (accessToken: string) => {
return tokens.find((token) => token.accessToken === accessToken);
},
getClient: async (clientId: string, clientSecret: string) => {
if (clientSecret === null) {
const client = clients.find((client) => client.id === clientId);
return client;
}const client = clients.find(
(client) =>
client.id === clientId && client.clientSecret === clientSecret,
);
return client;
},
getUser: async (username: string, password: string) => {
return users.find(
(user) => user.username === username && user.password === password,
);
},
saveToken: async (token: any, client: any, user: any) => {
const tokenData: Token = {
accessToken: token.accessToken,
accessTokenExpiresAt: token.accessTokenExpiresAt,
refreshToken: token.refreshToken,
refreshTokenExpiresAt: token.refreshTokenExpiresAt,
client: {
id: client.id,
grants: client.grants,
},
user: {
id: user.id,
},
};tokens.push(tokenData);
return tokenData;
},//#endregion
//#region authorization code
saveAuthorizationCode: async (code: any, client: any, user: any) => {
codes.push({
...code,
client: {
id: client.id,
grants: client.grants,
},
user: {
id: user.id,
},
});
return code;
},getAuthorizationCode: async (authorizationCode: string) => {
const a = codes.find(
(code) => code.authorizationCode === authorizationCode,
);
return a;
},
revokeAuthorizationCode: async (code: any) => {
const index = codes.findIndex(
(c) => c.authorizationCode === code.authorizationCode,
);
codes.splice(index, 1);
return true;
},//#endregion
//#region client credentials
getUserFromClient: async (client: any) => {
// TODO: find related user
return users[0];
},//#endregion
//#region refresh token
getRefreshToken: async (refreshToken: string) => {
return tokens.find((token) => token.refreshToken === refreshToken);
},revokeToken: async (token: any) => {
const index = tokens.findIndex(
(t) => t.accessToken === token.accessToken,
);
tokens.splice(index, 1);
return true;
},//#endregion
},
};new Elysia()
.use(oauth2(oauth2Opts)) // oauth2 wrapper
.post(
// getting token
'/oauth2/token',
({ oauth2, ...payload }) => {
return oauth2.token(payload);
},
{
headers: t.Object({
// Example: "Basic " + Buffer.from("clientId1:client@secret").toString("base64"),
authorization: t.String(),
}),
body: t.Object({
username: t.Optional(t.String()),
password: t.Optional(t.String()),
grant_type: t.String({
examples: [
'password',
'refresh_token',
'client_credentials',
'authorization_code',
],
}),
code: t.Optional(t.String()),
redirect_uri: t.Optional(t.String()),
refresh_token: t.Optional(t.String()),
}),
},
)
.post(
// verifing token
'/oauth2/authenticate',
({ oauth2, ...payload }) => {
return oauth2.authenticate(payload);
},
{
headers: t.Object({
// Example: "Basic " + Buffer.from("clientId1:client@secret").toString("base64"),
authorization: t.String(),
}),
body: t.Object({
token: t.String(),
}),
},
)
.post(
// checking credentials
'/oauth2/authorize',
({ oauth2, ...payload }) => {
return oauth2.authorize(payload, {
// TODO: implement.
authenticateHandler: {
handle: (request: any) => {
return { id: '123' };
},
},
});
},
{
body: t.Object({
client_id: t.String(),
response_type: t.String({
examples: ['code', 'token'],
}),
state: t.String(),
redirect_uri: t.String(),
}),
},
)
.listen(3000);
```