Compare commits
32 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
850860a335
|
|||
|
ecb9a95397
|
|||
|
447e5768a4
|
|||
| 117c78e7a9 | |||
|
95ed9bf158
|
|||
|
9dc3520989
|
|||
|
62c50964fa
|
|||
|
392414354b
|
|||
|
224eeae092
|
|||
|
64c4345c28
|
|||
|
6921bea5b5
|
|||
|
ebe086e70f
|
|||
|
af943c1e84
|
|||
|
50af0b3065
|
|||
|
50e691cf05
|
|||
|
e706ef1924
|
|||
|
0ad99d3f0a
|
|||
|
bb7b15351d
|
|||
|
bc73ab271b
|
|||
|
b1b1f3e0b9
|
|||
|
30c86accf6
|
|||
|
0882e8754e
|
|||
|
73ac3d2dc6
|
|||
| 5500a5644d | |||
|
4f00ef6ae4
|
|||
|
6ef8c553ce
|
|||
|
f2dce58f5e
|
|||
|
9c4e47fb72
|
|||
|
b969adbc2e
|
|||
|
1fa308b2a9
|
|||
|
f0c2c8d855
|
|||
|
19b6de68cf
|
6
.build.yarnrc.yml
Normal file
6
.build.yarnrc.yml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
nodeLinker: node-modules
|
||||||
|
npmScopes:
|
||||||
|
mifi:
|
||||||
|
npmRegistryServer: https://git.mifi.dev/api/packages/mifi/npm/
|
||||||
|
npmAuthToken: e35a182b01581790f91996594bc16e5c6a89c71c # ${NPM_TOKEN}
|
||||||
|
npmAlwaysAuth: true
|
||||||
280
.drone.yml
Normal file
280
.drone.yml
Normal file
@@ -0,0 +1,280 @@
|
|||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: Test Pipeline
|
||||||
|
|
||||||
|
workspace:
|
||||||
|
path: /drone/auth
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: yarn install
|
||||||
|
image: node:20-bullseye-slim
|
||||||
|
environment:
|
||||||
|
YARN_VERSION: 3.5.0
|
||||||
|
commands:
|
||||||
|
- yarn set version stable
|
||||||
|
- yarn install
|
||||||
|
volumes:
|
||||||
|
- name: yarnrc
|
||||||
|
path: /drone/auth/.yarnrc.yml
|
||||||
|
- name: Code Style Checks
|
||||||
|
image: node:latest
|
||||||
|
commands:
|
||||||
|
- yarn prettier
|
||||||
|
- name: Lint
|
||||||
|
image: node:latest
|
||||||
|
commands:
|
||||||
|
- yarn lint
|
||||||
|
- name: Unit Tests
|
||||||
|
image: node:latest
|
||||||
|
commands:
|
||||||
|
- yarn test
|
||||||
|
- name: Send Test Status Notification
|
||||||
|
image: plugins/webhook
|
||||||
|
settings:
|
||||||
|
urls: https://lab.mifi.dev/hooks/9p65zpagctgkmndo8nwwm4199r
|
||||||
|
content_type: application/json
|
||||||
|
template: |
|
||||||
|
{
|
||||||
|
"icon_url":"https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/thumbs/120/apple/198/freezing-face_1f976.png",
|
||||||
|
"text": "[{{ repo.name }} - Build # {{ build.number }}] Code Quality Checks {{ build.status }} {{#success build.status}}:tada:{{else}}:poop:{{/success}}",
|
||||||
|
"username":"DroneBot"
|
||||||
|
}
|
||||||
|
when:
|
||||||
|
status:
|
||||||
|
- success
|
||||||
|
- failure
|
||||||
|
- name: Build
|
||||||
|
image: node:latest
|
||||||
|
commands:
|
||||||
|
- yarn build:production
|
||||||
|
- name: Send Build Status Notifications
|
||||||
|
image: plugins/webhook
|
||||||
|
settings:
|
||||||
|
urls: https://lab.mifi.dev/hooks/9p65zpagctgkmndo8nwwm4199r
|
||||||
|
content_type: application/json
|
||||||
|
template: |
|
||||||
|
{
|
||||||
|
"icon_url":"https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/thumbs/120/apple/198/freezing-face_1f976.png",
|
||||||
|
"text": "[{{ repo.name }} - Build # {{ build.number }}] Build package {{ build.status }} {{#success build.status}}:tada:{{else}}:poop:{{/success}}",
|
||||||
|
"username":"DroneBot"
|
||||||
|
}
|
||||||
|
when:
|
||||||
|
status:
|
||||||
|
- success
|
||||||
|
- failure
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
- name: yarnrc
|
||||||
|
host:
|
||||||
|
path: /volume1/docker/.yarnrc.yml
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
branch:
|
||||||
|
- main
|
||||||
|
- develop
|
||||||
|
event:
|
||||||
|
- pull_request
|
||||||
|
|
||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: Publish Pipeline
|
||||||
|
|
||||||
|
workspace:
|
||||||
|
path: /drone/auth
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Build Package
|
||||||
|
image: node:20-bullseye-slim
|
||||||
|
environment:
|
||||||
|
YARN_VERSION: 3.5.0
|
||||||
|
commands:
|
||||||
|
- yarn set version stable
|
||||||
|
- yarn install
|
||||||
|
- yarn build:package
|
||||||
|
volumes:
|
||||||
|
- name: yarnrc
|
||||||
|
path: /drone/auth/.yarnrc.yml
|
||||||
|
- name: Publish NPM
|
||||||
|
image: node:20-bullseye-slim
|
||||||
|
commands:
|
||||||
|
- yarn npm publish
|
||||||
|
volumes:
|
||||||
|
- name: npmrc
|
||||||
|
path: /drone/auth/.npmrc
|
||||||
|
- name: yarnrc
|
||||||
|
path: /drone/auth/.yarnrc.yml
|
||||||
|
- name: Report NPM Publish Status
|
||||||
|
image: plugins/webhook
|
||||||
|
settings:
|
||||||
|
urls: https://lab.mifi.dev/hooks/ccw34hdf7tgbjmzp96nptn938r
|
||||||
|
content_type: application/json
|
||||||
|
template: |
|
||||||
|
{
|
||||||
|
"icon_url":"https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/thumbs/120/apple/198/freezing-face_1f976.png",
|
||||||
|
"text": "[{{ repo.name }} - New npm package release {{tag}} from # {{ build.number }}] Deploy {{ build.status }} {{#success build.status}}:tada:{{else}}:poop:{{/success}}",
|
||||||
|
"username":"DroneBot"
|
||||||
|
}
|
||||||
|
when:
|
||||||
|
status:
|
||||||
|
- success
|
||||||
|
- failure
|
||||||
|
- name: Publish Image
|
||||||
|
image: plugins/docker
|
||||||
|
settings:
|
||||||
|
auto_tag: true
|
||||||
|
repo: git.mifi.dev/mifi/auth-service
|
||||||
|
registry: git.mifi.dev
|
||||||
|
build_args:
|
||||||
|
- MONGO_ENTRY_FILE=latest
|
||||||
|
- MONGO_VERSION=latest
|
||||||
|
- NPM_TOKEN:
|
||||||
|
from_secret: reg_token
|
||||||
|
ssh-agent-key:
|
||||||
|
from_secret: reg_token
|
||||||
|
username: <token>
|
||||||
|
password:
|
||||||
|
from_secret: reg_token
|
||||||
|
secrets: [reg_token]
|
||||||
|
- name: Report Image Publish Status
|
||||||
|
image: plugins/webhook
|
||||||
|
settings:
|
||||||
|
urls: https://lab.mifi.dev/hooks/ccw34hdf7tgbjmzp96nptn938r
|
||||||
|
content_type: application/json
|
||||||
|
template: |
|
||||||
|
{
|
||||||
|
"icon_url":"https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/thumbs/120/apple/198/freezing-face_1f976.png",
|
||||||
|
"text": "[{{ repo.name }} - New docker image release {{tag}} from # {{ build.number }}] Deploy {{ build.status }} {{#success build.status}}:tada:{{else}}:poop:{{/success}}",
|
||||||
|
"username":"DroneBot"
|
||||||
|
}
|
||||||
|
when:
|
||||||
|
status:
|
||||||
|
- success
|
||||||
|
- failure
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
- name: dockerconfig
|
||||||
|
host:
|
||||||
|
path: /volume1/docker/dockerconfig.json
|
||||||
|
- name: dockersock
|
||||||
|
host:
|
||||||
|
path: /var/run/docker.sock
|
||||||
|
- name: npmrc
|
||||||
|
host:
|
||||||
|
path: /volume1/docker/beethoven/labs-auth/.npmrc
|
||||||
|
- name: yarnrc
|
||||||
|
host:
|
||||||
|
path: /volume1/docker/.yarnrc.yml
|
||||||
|
|
||||||
|
depends_on:
|
||||||
|
- Test Pipeline
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
event:
|
||||||
|
- tag
|
||||||
|
|
||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: Staging Deploy Pipeline
|
||||||
|
|
||||||
|
workspace:
|
||||||
|
path: /drone/auth
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Deploy Container
|
||||||
|
image: docker
|
||||||
|
privileged: true
|
||||||
|
environment:
|
||||||
|
CONTAINER_PREFIX: staging
|
||||||
|
HOST: area51.mifi.dev
|
||||||
|
ROUTE_PREFIX: /auth
|
||||||
|
PORT: 9001
|
||||||
|
commands:
|
||||||
|
- docker compose -f docker-compose.staging-build.yml build --pull --no-cache
|
||||||
|
- docker compose -f docker-compose.staging-build.yml up --remove-orphans --force-recreate --wait
|
||||||
|
volumes:
|
||||||
|
- name: env-secrets
|
||||||
|
path: /drone/auth/staging.env
|
||||||
|
- name: dockersock
|
||||||
|
path: /var/run/docker.sock
|
||||||
|
- name: dockerconfig
|
||||||
|
path: /drone/auth/.docker/config.json
|
||||||
|
- name: Send Status Notifications
|
||||||
|
image: plugins/webhook
|
||||||
|
privileged: true
|
||||||
|
settings:
|
||||||
|
urls: https://lab.mifi.dev/hooks/ccw34hdf7tgbjmzp96nptn938r
|
||||||
|
content_type: application/json
|
||||||
|
template: |
|
||||||
|
{
|
||||||
|
"icon_url":"https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/thumbs/120/apple/198/freezing-face_1f976.png",
|
||||||
|
"text": "[{{ repo.name }} - Build # {{ build.number }}] Staging Deploy {{ build.status }} {{#success build.status}}:tada:{{else}}:poop:{{/success}}",
|
||||||
|
"username":"DroneBot"
|
||||||
|
}
|
||||||
|
when:
|
||||||
|
status:
|
||||||
|
- success
|
||||||
|
- failure
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
- name: dockerconfig
|
||||||
|
host:
|
||||||
|
path: /volume1/docker/dockerconfig.json
|
||||||
|
- name: dockersock
|
||||||
|
host:
|
||||||
|
path: /var/run/docker.sock
|
||||||
|
- name: env-secrets
|
||||||
|
host:
|
||||||
|
path: /volume1/docker/beethoven/labs-auth/staging.env
|
||||||
|
|
||||||
|
depends_on:
|
||||||
|
- Test Pipeline
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
branch:
|
||||||
|
- develop
|
||||||
|
event:
|
||||||
|
- push
|
||||||
|
|
||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: Production Deploy Pipeline
|
||||||
|
|
||||||
|
workspace:
|
||||||
|
path: /drone/auth
|
||||||
|
|
||||||
|
clone:
|
||||||
|
disable: true
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Deploy Container
|
||||||
|
image: plugins/webhook
|
||||||
|
settings:
|
||||||
|
urls: https://portainer.mifi.dev/api/stacks/webhooks/968d2244-2548-4f0b-8c18-bbc9bc35305d
|
||||||
|
- name: Send Status Notifications
|
||||||
|
image: plugins/webhook
|
||||||
|
privileged: true
|
||||||
|
settings:
|
||||||
|
urls: https://lab.mifi.dev/hooks/ccw34hdf7tgbjmzp96nptn938r
|
||||||
|
content_type: application/json
|
||||||
|
template: |
|
||||||
|
{
|
||||||
|
"icon_url":"https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/thumbs/120/apple/198/freezing-face_1f976.png",
|
||||||
|
"text": "[{{ repo.name }} - Build # {{ build.number }}] Production Deploy {{ build.status }} {{#success build.status}}:tada:{{else}}:poop:{{/success}}",
|
||||||
|
"username":"DroneBot"
|
||||||
|
}
|
||||||
|
when:
|
||||||
|
status:
|
||||||
|
- success
|
||||||
|
- failure
|
||||||
|
|
||||||
|
depends_on:
|
||||||
|
- Publish Pipeline
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
event:
|
||||||
|
- promote
|
||||||
|
target:
|
||||||
|
- production
|
||||||
31
.env.dev
Normal file
31
.env.dev
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
HOST=localhost
|
||||||
|
PORT=9001
|
||||||
|
|
||||||
|
ROUTE_PREFIX=/auth
|
||||||
|
|
||||||
|
LOGIN_ROUTE=/login
|
||||||
|
RESET_ROUTE=/reset
|
||||||
|
|
||||||
|
DB_ADMIN_USERNAME=root
|
||||||
|
DB_ADMIN_PASSWORD=password
|
||||||
|
DB_USERNAME=user
|
||||||
|
DB_PASSWORD=password
|
||||||
|
DB_NAME=auth
|
||||||
|
|
||||||
|
MONGO_INITDB_ROOT_USERNAME=$DB_ADMIN_USERNAME
|
||||||
|
MONGO_INITDB_ROOT_PASSWORD=$DB_ADMIN_PASSWORD
|
||||||
|
MONGO_INITDB_DATABASE=$DB_NAME
|
||||||
|
|
||||||
|
SESSION_KEY=shjhakjfhfjdshjksdhfdshfhfduyeyb73te4
|
||||||
|
|
||||||
|
JWT_AUDIENCE=Grow.io
|
||||||
|
JWT_ISSUER=Grow Latch
|
||||||
|
JWT_SECRET=Th!sIs a d3v3lopm3nt server SEcr¢T.
|
||||||
|
|
||||||
|
LOGIN_VALID_TIMEOUT=12h
|
||||||
|
RESET_VALID_TIMEOUT=15m
|
||||||
|
DEFAULT_TOKEN_DAYS=1
|
||||||
|
|
||||||
|
CONTAINER_PREFIX=dev
|
||||||
|
SERVICE_NAME=auth-service
|
||||||
|
ENV=development
|
||||||
19
.eslintrc.js
Normal file
19
.eslintrc.js
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
/* eslint-env node */
|
||||||
|
module.exports = {
|
||||||
|
extends: [
|
||||||
|
'eslint:recommended',
|
||||||
|
'plugin:@typescript-eslint/recommended',
|
||||||
|
'plugin:import/errors',
|
||||||
|
'plugin:prettier/recommended',
|
||||||
|
'prettier',
|
||||||
|
],
|
||||||
|
parser: '@typescript-eslint/parser',
|
||||||
|
plugins: ['@typescript-eslint'],
|
||||||
|
settings: {
|
||||||
|
'import/parsers': {
|
||||||
|
'@typescript-eslint/parser': ['.ts', '.tsx'],
|
||||||
|
},
|
||||||
|
'import/resolver': 'typescript',
|
||||||
|
},
|
||||||
|
root: true,
|
||||||
|
};
|
||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -130,3 +130,6 @@ dist
|
|||||||
.yarn/install-state.gz
|
.yarn/install-state.gz
|
||||||
.pnp.*
|
.pnp.*
|
||||||
|
|
||||||
|
# Project
|
||||||
|
lib/
|
||||||
|
dist/
|
||||||
|
|||||||
11
.npmignore
Normal file
11
.npmignore
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
.build.yarnrc.yml
|
||||||
|
.drone.yml
|
||||||
|
.env*
|
||||||
|
.eslintrc*
|
||||||
|
.npmrc
|
||||||
|
.prettierrc*
|
||||||
|
.yarnrc.yml
|
||||||
|
babel.config.*
|
||||||
|
jest.config.*
|
||||||
|
tsconfig*.json
|
||||||
|
tslint.json
|
||||||
3
.npmrc
Normal file
3
.npmrc
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
registry=https://registry.npmjs.org
|
||||||
|
@mifi:registry=https://git.mifi.dev/api/packages/mifi/npm
|
||||||
|
//git.mifi.dev/api/packages/mifi/npm/:_authToken=${NPM_TOKEN}
|
||||||
7
.prettierrc.js
Normal file
7
.prettierrc.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
module.exports = {
|
||||||
|
parser: 'typescript',
|
||||||
|
printWidth: 120,
|
||||||
|
trailingComma: 'all',
|
||||||
|
tabWidth: 4,
|
||||||
|
singleQuote: true,
|
||||||
|
};
|
||||||
873
.yarn/releases/yarn-3.5.1.cjs
vendored
Executable file
873
.yarn/releases/yarn-3.5.1.cjs
vendored
Executable file
File diff suppressed because one or more lines are too long
3
.yarnrc.yml
Normal file
3
.yarnrc.yml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
nodeLinker: node-modules
|
||||||
|
|
||||||
|
yarnPath: .yarn/releases/yarn-3.5.1.cjs
|
||||||
41
Dockerfile
41
Dockerfile
@@ -1,28 +1,37 @@
|
|||||||
ARG ENV=production
|
ARG ENV=production
|
||||||
|
ARG MONGO_ENTRY_FILE
|
||||||
ARG MONGO_VERSION=latest
|
ARG MONGO_VERSION=latest
|
||||||
|
ARG NPM_TOKEN
|
||||||
ARG PORT=9001
|
ARG PORT=9001
|
||||||
|
ARG YARN_VERSION=3.5.0
|
||||||
|
|
||||||
## mongo build stage
|
## mongo build stage
|
||||||
FROM mongo:$MONGO_VERSION AS database
|
FROM mongo:${MONGO_VERSION} AS database
|
||||||
COPY docker-entrypoint-initdb.d/mongo-init-$MONGO_VERSION.sh ./docker-entrypoint-initdb.d/mongo-init.sh
|
ARG MONGO_ENTRY_FILE=${MONGO_ENTRY_FILE:-latest}
|
||||||
|
COPY docker-entrypoint-initdb.d/mongo-init-${MONGO_ENTRY_FILE}.sh ./docker-entrypoint-initdb.d/mongo-init.sh
|
||||||
|
|
||||||
## stage one, build the service
|
## stage one, build the service
|
||||||
FROM node:20-alpine AS build
|
FROM node:20-bullseye-slim AS build
|
||||||
ENV NODE_ENV development
|
ENV YARN_VERSION=${YARN_VERSION}
|
||||||
|
ENV NODE_ENV=development
|
||||||
|
ENV NPM_TOKEN=${NPM_TOKEN}
|
||||||
WORKDIR /home/node/app
|
WORKDIR /home/node/app
|
||||||
COPY package*.json ./
|
COPY .npmrc /root
|
||||||
COPY tsconfig.json ./
|
COPY .build.yarnrc.yml /root/.yarnrc.yml
|
||||||
COPY lib ./lib
|
COPY . .
|
||||||
RUN ls -a
|
RUN yarn set version stable && yarn install
|
||||||
RUN yarn install
|
RUN yarn build:production
|
||||||
RUN yarn build
|
|
||||||
|
|
||||||
## this is stage two , where the app actually runs
|
## this is stage two , where the app actually runs
|
||||||
FROM node:20-alpine AS containerize
|
FROM node:20-bullseye-slim AS containerize
|
||||||
ENV NODE_ENV $ENV
|
ENV YARN_VERSION=${YARN_VERSION}
|
||||||
|
ENV NODE_ENV=${ENV}
|
||||||
|
ENV NPM_TOKEN=${NPM_TOKEN}
|
||||||
WORKDIR /home/node/app
|
WORKDIR /home/node/app
|
||||||
COPY package*.json ./
|
COPY .npmrc /root
|
||||||
RUN yarn install --frozen-lockfile --production
|
COPY .build.yarnrc.yml /root/.yarnrc.yml
|
||||||
COPY --from=build /home/node/app/dist .
|
COPY package*.json yarn.lock ./
|
||||||
|
RUN yarn set version stable && yarn install && yarn cache clean
|
||||||
|
COPY --from=build /home/node/app/dist ./
|
||||||
EXPOSE $PORT
|
EXPOSE $PORT
|
||||||
CMD ["node","server/index.js"]
|
CMD ["node","index.js"]
|
||||||
|
|||||||
@@ -1,6 +1 @@
|
|||||||
module.exports = {
|
export const presets = [['@babel/preset-env', { targets: { node: 'current' } }], '@babel/preset-typescript'];
|
||||||
presets: [
|
|
||||||
['@babel/preset-env', { targets: { node: 'current' } }],
|
|
||||||
'@babel/preset-typescript',
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ services:
|
|||||||
context: .
|
context: .
|
||||||
target: database
|
target: database
|
||||||
args:
|
args:
|
||||||
MONGO_VERSION: 6.0.5
|
MONGO_VERSION: latest
|
||||||
ports:
|
ports:
|
||||||
- 27017:27017
|
- 27017:27017
|
||||||
networks:
|
networks:
|
||||||
@@ -17,7 +17,7 @@ services:
|
|||||||
- auth-db:/data/db
|
- auth-db:/data/db
|
||||||
- auth-db:/data/configdb
|
- auth-db:/data/configdb
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
image: mongo:latest
|
image: ${CONTAINER_PREFIX}-auth-service_mongo:${TAG:-latest}
|
||||||
auth-service:
|
auth-service:
|
||||||
env_file: .env.dev
|
env_file: .env.dev
|
||||||
build:
|
build:
|
||||||
@@ -26,6 +26,8 @@ services:
|
|||||||
args:
|
args:
|
||||||
- PORT
|
- PORT
|
||||||
- ENV
|
- ENV
|
||||||
|
- NPM_TOKEN
|
||||||
|
- YARN_VERSION=3.5.0
|
||||||
container_name: ${CONTAINER_PREFIX}-auth-service
|
container_name: ${CONTAINER_PREFIX}-auth-service
|
||||||
ports:
|
ports:
|
||||||
- 9001:9001
|
- 9001:9001
|
||||||
@@ -35,7 +37,7 @@ services:
|
|||||||
- labs-net
|
- labs-net
|
||||||
- backend
|
- backend
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
image: node:20-alpine
|
image: ${CONTAINER_PREFIX}-auth-service:${TAG:-latest}
|
||||||
depends_on:
|
depends_on:
|
||||||
- auth-service_mongo
|
- auth-service_mongo
|
||||||
networks:
|
networks:
|
||||||
|
|||||||
@@ -9,14 +9,15 @@ services:
|
|||||||
context: .
|
context: .
|
||||||
target: database
|
target: database
|
||||||
args:
|
args:
|
||||||
MONGO_VERSION: 4.4
|
- MONGO_ENTRY_FILE=4.4
|
||||||
|
- MONGO_VERSION=4.4
|
||||||
networks:
|
networks:
|
||||||
- auth-backend
|
- auth-backend
|
||||||
volumes:
|
volumes:
|
||||||
- 'auth-db:/data/db'
|
- 'auth-db:/data/db'
|
||||||
- 'auth-db:/data/configdb'
|
- 'auth-db:/data/configdb'
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
image: mongo:4.4
|
image: ${CONTAINER_PREFIX}auth-service_mongo:${DRONE_TAG:-latest}
|
||||||
auth-service:
|
auth-service:
|
||||||
container_name: ${CONTAINER_PREFIX}-auth-service
|
container_name: ${CONTAINER_PREFIX}-auth-service
|
||||||
env_file:
|
env_file:
|
||||||
@@ -42,7 +43,7 @@ services:
|
|||||||
- auth-backend
|
- auth-backend
|
||||||
- docknet
|
- docknet
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
image: node:20-alpine
|
image: ${CONTAINER_PREFIX}auth-service:${DRONE_TAG:-latest}
|
||||||
depends_on:
|
depends_on:
|
||||||
- auth-service_mongo
|
- auth-service_mongo
|
||||||
networks:
|
networks:
|
||||||
@@ -55,4 +56,4 @@ networks:
|
|||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
auth-db:
|
auth-db:
|
||||||
external: false
|
external: true
|
||||||
|
|||||||
@@ -3,41 +3,57 @@ version: '3.8'
|
|||||||
services:
|
services:
|
||||||
auth-service_mongo:
|
auth-service_mongo:
|
||||||
container_name: ${CONTAINER_PREFIX}-auth-service_mongo
|
container_name: ${CONTAINER_PREFIX}-auth-service_mongo
|
||||||
env_file:
|
|
||||||
- staging.env
|
|
||||||
networks:
|
networks:
|
||||||
|
- auth-backend
|
||||||
- docknet
|
- docknet
|
||||||
volumes:
|
volumes:
|
||||||
- auth-db:/data
|
- auth-db:/data/db
|
||||||
- ./mongo-init.js:/docker-entrypoint-initdb.d/mongo-init.js:ro
|
- auth-db:/data/configdb
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
image: mongo:4.4
|
image: mongo:${MONGO_VERSION}
|
||||||
auth-service:
|
auth-service:
|
||||||
env_file:
|
|
||||||
- staging.env
|
|
||||||
container_name: ${CONTAINER_PREFIX}-auth-service
|
container_name: ${CONTAINER_PREFIX}-auth-service
|
||||||
environment:
|
environment:
|
||||||
- DB_HOST=${CONTAINER_PREFIX}-auth-service_mongo
|
- DB_HOST=${CONTAINER_PREFIX}-auth-service_mongo
|
||||||
|
- DB_NAME=${DB_NAME}
|
||||||
|
- DB_PASSWORD=${DB_PASSWORD}
|
||||||
|
- DB_USERNAME=${DB_USERNAME}
|
||||||
|
- DEFAULT_TOKEN_DAYS=${DEFAULT_TOKEN_DAYS}
|
||||||
|
- JWT_AUDIENCE=${JWT_AUDIENCE}
|
||||||
|
- JWT_ISSUER=${JWT_ISSUER}
|
||||||
|
- JWT_SECRET=${JWT_SECRET}
|
||||||
|
- LOGIN_ROUTE=${LOGIN_ROUTE}
|
||||||
|
- LOGIN_VALID_TIME=${LOGIN_VALID_TIME}
|
||||||
|
- PORT=${PORT}
|
||||||
|
- RESET_ROUTE=${RESET_ROUTE}
|
||||||
|
- RESET_VALID_MINUTES=${RESET_VALID_MINUTES}
|
||||||
|
- ROUTE_PREFIX=${ROUTE_PREFIX}
|
||||||
|
- SESSION_KEY=${SESSION_KEY}
|
||||||
labels:
|
labels:
|
||||||
- 'traefik.enable=true'
|
- 'traefik.enable=true'
|
||||||
- 'traefik.http.routers.grow.rule=Host(`${HOST}`) && Path(`${ROUTE_PREFIX}`)'
|
- 'traefik.docker.network=docknet'
|
||||||
- 'traefik.http.routers.grow.entrypoints=websecure'
|
- 'traefik.http.routers.auth.rule=Host(`${HOST}`) && PathPrefix(`${ROUTE_PREFIX}`)'
|
||||||
- 'traefik.http.routers.grow.tls=true'
|
- 'traefik.http.routers.auth.entrypoints=websecure'
|
||||||
- 'traefik.http.routers.grow.tls.certresolver=letsencrypt'
|
- 'traefik.http.routers.auth.tls=true'
|
||||||
- 'traefik.http.routers.grow.service=grow-service'
|
- 'traefik.http.routers.auth.tls.certresolver=letsencrypt'
|
||||||
- 'traefik.http.services.grow-service.loadbalancer.server.port=${PORT}'
|
- 'traefik.http.routers.auth.service=auth-service'
|
||||||
|
- 'traefik.http.services.auth-service.loadbalancer.server.port=${PORT}'
|
||||||
networks:
|
networks:
|
||||||
|
- auth-backend
|
||||||
- docknet
|
- docknet
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
depends_on:
|
depends_on:
|
||||||
- auth-service_mongo
|
- auth-service_mongo
|
||||||
image: git.mifi.dev/mifi/mifi/auth:latest
|
image: git.mifi.dev/mifi/auth-service:latest
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
|
auth-backend:
|
||||||
|
driver: bridge
|
||||||
|
external: false
|
||||||
docknet:
|
docknet:
|
||||||
name: docknet
|
name: docknet
|
||||||
external: true
|
external: true
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
auth-db:
|
auth-db:
|
||||||
external: false
|
external: true
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
export enum Action {
|
|
||||||
AUTHENTICATE = 'AUTHENTICATE',
|
|
||||||
AUTHENTICATE_FAILURE = 'AUTHENTICATE_FAILURE',
|
|
||||||
CREATE = 'CREATE',
|
|
||||||
DELETE = 'DELETE',
|
|
||||||
RESET = 'RESET',
|
|
||||||
RESET_REQUEST = 'RESET_REQUEST',
|
|
||||||
UPDATE = 'UPDATE',
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
export enum Status {
|
|
||||||
ACTIVE,
|
|
||||||
BLOCK_HARD,
|
|
||||||
BLOCK_SOFT,
|
|
||||||
DELETED,
|
|
||||||
INACTIVE,
|
|
||||||
UNVERIFIED,
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
export const DB_HOST = process.env.DB_HOST;
|
|
||||||
export const DB_PORT = process.env.DB_PORT || 27017;
|
|
||||||
export const DB_USERNAME = process.env.DB_USERNAME;
|
|
||||||
export const DB_PASSWORD = process.env.DB_PASSWORD;
|
|
||||||
export const DB_NAME = process.env.DB_NAME;
|
|
||||||
|
|
||||||
export const COLL_AUTH = 'Auth';
|
|
||||||
export const COLL_LOG = 'Log';
|
|
||||||
export const COLL_STRATEGY = 'Strategy';
|
|
||||||
export const COLL_TOKEN = 'Token';
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
export const PACKAGE_NAME = '@mifi/auth';
|
|
||||||
export const PORT = process.env.PORT || 9000;
|
|
||||||
|
|
||||||
export const SESSION_KEY = process.env.SESSION_KEY || 'secret-key';
|
|
||||||
|
|
||||||
export const JWT_AUDIENCE = process.env.JWT_AUDIENCE || 'mifi.dev';
|
|
||||||
export const JWT_ISSUER = process.env.JWT_ISSUER || PACKAGE_NAME;
|
|
||||||
export const JWT_SECRET = process.env.JWT_SECRET || 'secret';
|
|
||||||
|
|
||||||
export const LOGIN_VALID_TIMEOUT = process.env.LOGIN_VALID_TIMEOUT || '12h'; // ###d|h|m
|
|
||||||
export const RESET_VALID_TIMEOUT = process.env.RESET_VALID_TIMEOUT || '15m'; // ###d|h|m
|
|
||||||
export const VERIFY_VALID_TIMEOUT = process.env.VERIFY_VALID_TIMEOUT || '60d'; // ###d|h|m
|
|
||||||
export const DEFAULT_TOKEN_DAYS = process.env.DEFAULT_TOKEN_DAYS || 365;
|
|
||||||
|
|
||||||
export const ROUTE_PREFIX = process.env.ROUTE_PREFIX || '/auth';
|
|
||||||
export const LOGIN_ROUTE = process.env.LOGIN_ROUTE || '/login';
|
|
||||||
export const RESET_ROUTE = process.env.RESET_ROUTE || '/reset';
|
|
||||||
export const VERIFICATION_ROUTE = process.env.VERIFICATION_ROUTE || '/verification';
|
|
||||||
|
|
||||||
export const REQUIRE_VERIFICATION = process.env.REQUIRE_VERIFICATION || true;
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
export enum STRATEGIES {
|
|
||||||
LOCAL,
|
|
||||||
APPLE,
|
|
||||||
FACEBOOK,
|
|
||||||
FIDO2,
|
|
||||||
GOOGLE,
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
export enum TokenType {
|
|
||||||
RESET = 'RESET',
|
|
||||||
VERIFICATION = 'VERIFICATION',
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
import { Middleware } from 'koa';
|
|
||||||
|
|
||||||
import { LOGIN_ROUTE } from '../constants/env';
|
|
||||||
|
|
||||||
export const authenticated = (): Middleware => {
|
|
||||||
return (ctx, next) => {
|
|
||||||
if (ctx.isAuthenticated()) {
|
|
||||||
return next();
|
|
||||||
} else {
|
|
||||||
ctx.redirect(process.env.LOGIN_ROUTE || LOGIN_ROUTE);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
import { Context, Next } from 'koa';
|
|
||||||
|
|
||||||
export const performanceLogger = async (ctx: Context, next: Next) => {
|
|
||||||
await next();
|
|
||||||
const rt = ctx.response.get('X-Response-Time');
|
|
||||||
console.log(`${ctx.method} ${ctx.url} - ${rt}`);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const performanceTimer = async (ctx: Context, next: Next) => {
|
|
||||||
const start = Date.now();
|
|
||||||
await next();
|
|
||||||
const ms = Date.now() - start;
|
|
||||||
ctx.set('X-Response-Time', `${ms}ms`);
|
|
||||||
};
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
import passport from 'koa-passport';
|
|
||||||
import { Types } from 'mongoose';
|
|
||||||
|
|
||||||
import { AuthDocument } from '@mifi/services-common/lib/db/schema/auth';
|
|
||||||
import { readOneByRecord } from '@mifi/services-common/lib/db/dao/readOneByRecord';
|
|
||||||
import { readOneById } from '@mifi/services-common/lib/db/dao/readOneById';
|
|
||||||
|
|
||||||
import LocalStrategy from './strategies/local';
|
|
||||||
import JwtStrategy from './strategies/jwt';
|
|
||||||
|
|
||||||
passport.use(LocalStrategy);
|
|
||||||
passport.use(JwtStrategy);
|
|
||||||
|
|
||||||
passport.serializeUser((user, done) => {
|
|
||||||
done(null, (user as AuthDocument).record || (user as AuthDocument).id);
|
|
||||||
});
|
|
||||||
|
|
||||||
passport.deserializeUser(async (id, done) => {
|
|
||||||
const user = await readOneByRecord(<Types.ObjectId>id).catch(async () => await readOneById(<Types.ObjectId>id));
|
|
||||||
done(user ? null : 'user not found', user);
|
|
||||||
});
|
|
||||||
|
|
||||||
export default passport;
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
import { ExtractJwt, Strategy as JwtStrategy } from 'passport-jwt';
|
|
||||||
|
|
||||||
import { readOneByRecord } from '@mifi/services-common/lib/db/dao/readOneByRecord';
|
|
||||||
|
|
||||||
import { JWT_SECRET } from '../../constants/env';
|
|
||||||
|
|
||||||
const opts = {
|
|
||||||
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
|
||||||
secretOrKey: JWT_SECRET,
|
|
||||||
issuer: process.env.JWT_ISSUER,
|
|
||||||
audience: process.env.JWT_AUDIENCE,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default new JwtStrategy(opts, async ({ sub }, done) => {
|
|
||||||
const auth = await readOneByRecord(sub);
|
|
||||||
return done(null, auth || false);
|
|
||||||
});
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
// eslint-disable-next-line import/named
|
|
||||||
import { Strategy as LocalStrategy } from 'passport-local';
|
|
||||||
|
|
||||||
import { authenticate } from '@mifi/services-common/lib/db/api/authenticate';
|
|
||||||
|
|
||||||
export default new LocalStrategy(async (username: string, password: string, done: any) => {
|
|
||||||
const user = await authenticate(username, password);
|
|
||||||
done(null, user);
|
|
||||||
});
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
import jwt from 'jsonwebtoken';
|
|
||||||
import { JWT_AUDIENCE, JWT_ISSUER, JWT_SECRET } from '../constants/env';
|
|
||||||
export interface TokenProps {
|
|
||||||
aud?: string;
|
|
||||||
exp?: number | Date;
|
|
||||||
iss?: string;
|
|
||||||
sub: string | null;
|
|
||||||
[key: string]: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type SignProps = string | TokenProps | void;
|
|
||||||
|
|
||||||
export const sign = (props: SignProps) => {
|
|
||||||
const today = new Date();
|
|
||||||
const { sub = null, ...rest }: TokenProps =
|
|
||||||
typeof props === 'string' || typeof props === 'undefined' ? { sub: props || null } : props;
|
|
||||||
let { exp } = rest;
|
|
||||||
if (!exp) {
|
|
||||||
exp = new Date(today);
|
|
||||||
exp.setDate(today.getDate() + parseInt(process.env.JWT_DAYS_VALID as string));
|
|
||||||
exp = exp.getTime() / 1000;
|
|
||||||
}
|
|
||||||
return jwt.sign(
|
|
||||||
{
|
|
||||||
exp,
|
|
||||||
sub,
|
|
||||||
aud: rest.aud || JWT_AUDIENCE,
|
|
||||||
iat: today.getTime(),
|
|
||||||
iss: rest.iss || JWT_ISSUER,
|
|
||||||
},
|
|
||||||
JWT_SECRET,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const verify = (token: string) => jwt.verify(token, JWT_SECRET);
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
export const parseTimeoutToMs = (timeout: string) => {
|
|
||||||
const match = timeout.match(/(?<number>\d+)(?<unit>d|h|m)/gi)?.groups || {};
|
|
||||||
const { number, unit } = match;
|
|
||||||
switch (unit) {
|
|
||||||
case 'd':
|
|
||||||
return 1000 * 60 * 60 * 24 * parseInt(number);
|
|
||||||
case 'h':
|
|
||||||
return 1000 * 60 * 60 * parseInt(number);
|
|
||||||
case 'm':
|
|
||||||
default:
|
|
||||||
return 1000 * 60 * parseInt(number) || 1;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
import { pbkdf2Sync, randomBytes } from 'crypto';
|
|
||||||
|
|
||||||
export const encrypt = (password: string) => {
|
|
||||||
const salt = randomBytes(16).toString('hex');
|
|
||||||
const hash = pbkdf2Sync(password, salt, 10000, 512, 'sha512').toString('hex');
|
|
||||||
return `${salt}:${hash}`;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const verify = (test: string, secret: string) => {
|
|
||||||
const [salt, hash] = secret.split(':');
|
|
||||||
return pbkdf2Sync(test, salt, 10000, 512, 'sha512').toString('hex') === hash;
|
|
||||||
};
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
import { sign } from './jwt';
|
|
||||||
import { LOGIN_VALID_TIMEOUT } from '../constants/env';
|
|
||||||
import { Status } from '../constants/auth';
|
|
||||||
import { parseTimeoutToMs } from './parseTimeoutToMs';
|
|
||||||
|
|
||||||
export const generateLoginToken = (sub: string, status: Status) =>
|
|
||||||
sign({
|
|
||||||
sub,
|
|
||||||
status,
|
|
||||||
exp: Date.now() + parseTimeoutToMs(LOGIN_VALID_TIMEOUT),
|
|
||||||
});
|
|
||||||
70
package.json
70
package.json
@@ -1,17 +1,19 @@
|
|||||||
{
|
{
|
||||||
"name": "@mifi/auth-service",
|
"name": "@mifi/auth-service",
|
||||||
"version": "1.0.0",
|
"version": "1.0.17",
|
||||||
"author": "mifi (Mike Fitzpatrick)",
|
"author": "mifi (Mike Fitzpatrick)",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsc",
|
"build": "tsc",
|
||||||
"build:production": "tsc -p .",
|
"build:package": "tsc -p ./tsconfig.package.json",
|
||||||
|
"build:production": "tsc -p ./tsconfig.production.json",
|
||||||
"format": "prettier:fix && lint:fix",
|
"format": "prettier:fix && lint:fix",
|
||||||
"lint": "eslint --ext .ts,.tsx lib/",
|
"lint": "eslint --ext .ts,.tsx src/",
|
||||||
"lint:fix": "eslint --fix --ext .ts,.tsx lib/",
|
"lint:fix": "eslint --fix --ext .ts,.tsx src/",
|
||||||
"prettier": "prettier --check 'lib/**/*.ts'",
|
"prettier": "prettier --check 'src/**/*.ts'",
|
||||||
"prettier:fix": "prettier --write 'lib/**/*.ts'",
|
"prettier:fix": "prettier --write 'src/**/*.ts'",
|
||||||
"serve": "node dist/lib/index.js",
|
"serve-dev": "ts-node src/index.ts",
|
||||||
|
"serve": "node lib/index.js",
|
||||||
"start": "nodemon",
|
"start": "nodemon",
|
||||||
"test": "jest --passWithNoTests"
|
"test": "jest --passWithNoTests"
|
||||||
},
|
},
|
||||||
@@ -19,23 +21,18 @@
|
|||||||
"@babel/core": "^7.21.8",
|
"@babel/core": "^7.21.8",
|
||||||
"@babel/preset-env": "^7.21.5",
|
"@babel/preset-env": "^7.21.5",
|
||||||
"@babel/preset-typescript": "^7.21.5",
|
"@babel/preset-typescript": "^7.21.5",
|
||||||
"@tsconfig/node16": "^1.0.3",
|
"@simplewebauthn/typescript-types": "^7.x.x",
|
||||||
|
"@tsconfig/node16": "^1.0.4",
|
||||||
"@types/jest": "^29.5.1",
|
"@types/jest": "^29.5.1",
|
||||||
"@types/jsonwebtoken": "^9.0.1",
|
"@types/jsonwebtoken": "^9.0.1",
|
||||||
"@types/koa": "^2.13.5",
|
"@types/koa": "^2.13.5",
|
||||||
"@types/koa-bodyparser": "^4.3.10",
|
"@types/koa-bodyparser": "^4.3.10",
|
||||||
"@types/koa-cookie": "^1.0.0",
|
"@types/koa-cookie": "^1.0.0",
|
||||||
"@types/koa-passport": "^4.0.3",
|
|
||||||
"@types/koa-router": "^7.4.4",
|
"@types/koa-router": "^7.4.4",
|
||||||
"@types/koa-session": "^5.10.6",
|
"@types/koa-session": "^5.10.6",
|
||||||
"@types/luxon": "^3.2.0",
|
"@types/luxon": "^3.2.0",
|
||||||
"@types/node": "^18.14.0",
|
"@types/node": "^18.14.0",
|
||||||
"@types/passport": "^1.0.12",
|
"@types/uuid": "^9.0.1",
|
||||||
"@types/passport-facebook": "^2.1.11",
|
|
||||||
"@types/passport-fido2-webauthn": "^0.1.0",
|
|
||||||
"@types/passport-google-oauth": "^1.0.42",
|
|
||||||
"@types/passport-jwt": "^3.0.8",
|
|
||||||
"@types/passport-local": "^1.0.35",
|
|
||||||
"@typescript-eslint/eslint-plugin": "^5.59.2",
|
"@typescript-eslint/eslint-plugin": "^5.59.2",
|
||||||
"@typescript-eslint/parser": "^5.59.2",
|
"@typescript-eslint/parser": "^5.59.2",
|
||||||
"babel-jest": "^29.5.0",
|
"babel-jest": "^29.5.0",
|
||||||
@@ -52,33 +49,32 @@
|
|||||||
"prettier-eslint": "^15.0.1",
|
"prettier-eslint": "^15.0.1",
|
||||||
"prettier-eslint-cli": "^7.1.0",
|
"prettier-eslint-cli": "^7.1.0",
|
||||||
"reflect-metadata": "^0.1.13",
|
"reflect-metadata": "^0.1.13",
|
||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.x.x",
|
||||||
"typescript": "^4.9.5"
|
"typescript": "^4.x.x"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mifi/auth-db": "^1.0.0",
|
"@mifi/auth-common": "^1.0.15",
|
||||||
"@simplewebauthn/server": "^7.2.0",
|
"@mifi/auth-db": "^1.0.20",
|
||||||
"dotenv": "^16.0.3",
|
"@mifi/auth-gatekeeper": "^1.0.12",
|
||||||
"http-status-codes": "^2.2.0",
|
"@mifi/services-common": "^1.0.16",
|
||||||
"jsonwebtoken": "^9.0.0",
|
"@simplewebauthn/server": "^7.x.x",
|
||||||
"koa": "^2.14.1",
|
"base64url": "^3.x.x",
|
||||||
"koa-bodyparser": "^4.3.0",
|
"dotenv": "^16.x.x",
|
||||||
"koa-cookie": "^1.0.0",
|
"http-status-codes": "^2.x.x",
|
||||||
"koa-passport": "^6.0.0",
|
"jsonwebtoken": "^9.x.x",
|
||||||
"koa-router": "^12.0.0",
|
"koa": "^2.x.x",
|
||||||
"koa-session": "^6.4.0",
|
"koa-bodyparser": "^4.x.x",
|
||||||
|
"koa-compose": "^4.1.0",
|
||||||
|
"koa-cookie": "^1.x.x",
|
||||||
|
"koa-router": "^12.x.x",
|
||||||
|
"koa-session": "^6.x.x",
|
||||||
"luxon": "^3.3.0",
|
"luxon": "^3.3.0",
|
||||||
"passport": "^0.6.0",
|
"uuid": "^9.x.x"
|
||||||
"passport-facebook": "^3.0.0",
|
|
||||||
"passport-fido2-webauthn": "^0.1.0",
|
|
||||||
"passport-google-oauth": "^2.0.0",
|
|
||||||
"passport-http-bearer": "^1.0.1",
|
|
||||||
"passport-jwt": "^4.0.1",
|
|
||||||
"passport-local": "^1.0.0"
|
|
||||||
},
|
},
|
||||||
"description": "",
|
"description": "",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://git.mifi.dev/mifi/auth-api.git"
|
"url": "https://git.mifi.dev/mifi/auth-service.git"
|
||||||
}
|
},
|
||||||
|
"packageManager": "yarn@3.5.1"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,11 +3,12 @@ import bodyparser from 'koa-bodyparser';
|
|||||||
import cookie from 'koa-cookie';
|
import cookie from 'koa-cookie';
|
||||||
import session from 'koa-session';
|
import session from 'koa-session';
|
||||||
|
|
||||||
import passport from './passport';
|
import { gatekeeper } from '@mifi/auth-gatekeeper/lib';
|
||||||
|
|
||||||
import { performanceLogger, performanceTimer } from './middleware/performance';
|
import { performanceLogger, performanceTimer } from './middleware/performance';
|
||||||
import { errorHandler } from './middleware/errorHandler';
|
import { errorHandler } from './middleware/errorHandler';
|
||||||
import { authRouter } from './controllers/auth';
|
import { authRouter } from './controllers/auth';
|
||||||
import { SESSION_KEY } from '../constants/env';
|
import { SESSION_KEY } from './env';
|
||||||
|
|
||||||
const app: Koa = new Koa();
|
const app: Koa = new Koa();
|
||||||
|
|
||||||
@@ -20,8 +21,8 @@ app.use(cookie());
|
|||||||
app.keys = [SESSION_KEY];
|
app.keys = [SESSION_KEY];
|
||||||
app.use(session({}, app));
|
app.use(session({}, app));
|
||||||
|
|
||||||
app.use(passport.initialize());
|
app.use(gatekeeper.initialize);
|
||||||
app.use(passport.session());
|
app.use(gatekeeper.jwt);
|
||||||
|
|
||||||
app.use(authRouter.routes());
|
app.use(authRouter.routes());
|
||||||
app.use(authRouter.allowedMethods());
|
app.use(authRouter.allowedMethods());
|
||||||
@@ -3,18 +3,18 @@ import Koa from 'koa';
|
|||||||
import Router from 'koa-router';
|
import Router from 'koa-router';
|
||||||
import { StringSchemaDefinition } from 'mongoose';
|
import { StringSchemaDefinition } from 'mongoose';
|
||||||
|
|
||||||
import { Auth } from '@mifi/services-common/lib/db';
|
import { ROUTE_PREFIX as prefix, RESET_ROUTE } from '@mifi/auth-common/lib/env/routes';
|
||||||
import { create } from '@mifi/services-common/lib/db/dao/create';
|
import { STRATEGIES } from '@mifi/auth-common/lib/enums/strategies';
|
||||||
import { resetPasswordPost } from '@mifi/services-common/lib/db/api/resetPasswordPost';
|
import { Auth } from '@mifi/auth-db/lib';
|
||||||
import { resetPasswordGet } from '@mifi/services-common/lib/db/api/resetPasswordGet';
|
import { deleteStrategy } from '@mifi/auth-db/lib/api/deleteStrategy';
|
||||||
import { deleteById } from '@mifi/services-common/lib/db/dao/deleteById';
|
import { resetPasswordGet } from '@mifi/auth-db/lib/api/resetPasswordGet';
|
||||||
import { deleteStrategy } from '@mifi/services-common/lib/db/api/deleteStrategy';
|
import { resetPasswordPost } from '@mifi/auth-db/lib/api/resetPasswordPost';
|
||||||
import { AuthDocument } from '@mifi/services-common/lib/db/schema/auth';
|
import { create } from '@mifi/auth-db/lib/dao/create';
|
||||||
|
import { deleteById } from '@mifi/auth-db/lib/dao/deleteById';
|
||||||
|
import { AuthDocument } from '@mifi/auth-db/lib/schema/auth';
|
||||||
|
import { gatekeeper } from '@mifi/auth-gatekeeper/lib';
|
||||||
|
|
||||||
import { ROUTE_PREFIX as prefix, RESET_ROUTE } from '../constants/env';
|
|
||||||
import passport from '../passport';
|
|
||||||
import { ErrorCodes, getErrorBody } from '../constants/errors';
|
import { ErrorCodes, getErrorBody } from '../constants/errors';
|
||||||
import { authenticated } from '../middleware/authenication';
|
|
||||||
|
|
||||||
const routerOpts: Router.IRouterOptions = { prefix };
|
const routerOpts: Router.IRouterOptions = { prefix };
|
||||||
const router: Router = new Router(routerOpts);
|
const router: Router = new Router(routerOpts);
|
||||||
@@ -34,20 +34,15 @@ router.post('/', async (ctx) => {
|
|||||||
ctx.body = { success: !!data, data };
|
ctx.body = { success: !!data, data };
|
||||||
});
|
});
|
||||||
|
|
||||||
router.delete('/strategy/:id', async (ctx) => {
|
router.delete('/strategy/:id', gatekeeper.authenticated, async (ctx) => {
|
||||||
ctx.body = { success: await deleteStrategy(ctx.params.id as StringSchemaDefinition) };
|
ctx.body = { success: await deleteStrategy(ctx.params.id as StringSchemaDefinition) };
|
||||||
});
|
});
|
||||||
|
|
||||||
router.delete('/:id', async (ctx) => {
|
router.delete('/:id', gatekeeper.authenticated, async (ctx) => {
|
||||||
ctx.body = { success: await deleteById(ctx.params.id as StringSchemaDefinition) };
|
ctx.body = { success: await deleteById(ctx.params.id as StringSchemaDefinition) };
|
||||||
});
|
});
|
||||||
|
|
||||||
router.post('/login', async (ctx, next) => {
|
router.post('/login', gatekeeper.authenticate(STRATEGIES[STRATEGIES.LOCAL]));
|
||||||
return passport.authenticate('local', (err, user) => {
|
|
||||||
ctx.body = user;
|
|
||||||
return user ? ctx.login(user) : ctx.throw(StatusCodes.UNAUTHORIZED);
|
|
||||||
})(ctx, next);
|
|
||||||
});
|
|
||||||
|
|
||||||
router.post(process.env.RESET_ROUTE || RESET_ROUTE, async (ctx) => {
|
router.post(process.env.RESET_ROUTE || RESET_ROUTE, async (ctx) => {
|
||||||
const { password, token, username } = ctx.request.body as { token?: string; password?: string; username?: string };
|
const { password, token, username } = ctx.request.body as { token?: string; password?: string; username?: string };
|
||||||
@@ -66,8 +61,8 @@ router.post(process.env.RESET_ROUTE || RESET_ROUTE, async (ctx) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
router.patch('/:record', authenticated(), (ctx: Koa.Context) => {
|
router.patch('/:record', gatekeeper.authenticated, (ctx: Koa.Context) => {
|
||||||
if (ctx.user !== ctx.param.record) {
|
if (ctx.user.record !== ctx.param.record) {
|
||||||
ctx.throw(StatusCodes.UNAUTHORIZED);
|
ctx.throw(StatusCodes.UNAUTHORIZED);
|
||||||
}
|
}
|
||||||
const data = Auth.findOneAndUpdate({ record: ctx.params.record });
|
const data = Auth.findOneAndUpdate({ record: ctx.params.record });
|
||||||
10
src/controllers/passkey.ts
Normal file
10
src/controllers/passkey.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
// import Router from 'koa-router';
|
||||||
|
|
||||||
|
// const routerOpts: Router.IRouterOptions = { prefix: '/passkey' };
|
||||||
|
// const router: Router = new Router(routerOpts);
|
||||||
|
|
||||||
|
// router.post('/login', async (ctx, next) => {});
|
||||||
|
|
||||||
|
// router.post('/login/challenge', async (ctx, next) => {});
|
||||||
|
|
||||||
|
// router.post('/signup/challenge', async (ctx, next) => {});
|
||||||
4
src/env.ts
Normal file
4
src/env.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
import { getEnvVar } from '@mifi/services-common/lib/utils/getEnvVar';
|
||||||
|
|
||||||
|
export const SESSION_KEY = getEnvVar<string>('SESSION_KEY', 'secret');
|
||||||
|
export const PORT = getEnvVar<number>('PORT', 9001);
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import app from './app';
|
import app from './app';
|
||||||
import { connection } from '../db';
|
import { connection } from '@mifi/auth-db/lib';
|
||||||
import { PORT } from '../constants/env';
|
import { PORT } from './env';
|
||||||
|
|
||||||
connection.then(
|
connection.then(
|
||||||
() => {
|
() => {
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
import { StatusCodes } from 'http-status-codes';
|
import { StatusCodes } from 'http-status-codes';
|
||||||
import { Context, Next } from 'koa';
|
import { Middleware } from 'koa';
|
||||||
|
|
||||||
export const errorHandler = async (ctx: Context, next: Next) => {
|
export const errorHandler: Middleware = async (ctx, next) => {
|
||||||
try {
|
try {
|
||||||
await next();
|
await next();
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
ctx.status = error.statusCode || error.status || StatusCodes.INTERNAL_SERVER_ERROR;
|
ctx.status = error.statusCode || error.status || StatusCodes.INTERNAL_SERVER_ERROR;
|
||||||
error.status = ctx.status;
|
error.status = ctx.status;
|
||||||
14
src/middleware/performance.ts
Normal file
14
src/middleware/performance.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { Middleware } from 'koa';
|
||||||
|
|
||||||
|
export const performanceLogger: Middleware = async (ctx, next) => {
|
||||||
|
await next();
|
||||||
|
const rt = ctx.response.get('X-Response-Time');
|
||||||
|
console.debug(`${ctx.method} ${ctx.url} - ${rt}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const performanceTimer: Middleware = async (ctx, next) => {
|
||||||
|
const start = Date.now();
|
||||||
|
await next();
|
||||||
|
const ms = Date.now() - start;
|
||||||
|
ctx.set('X-Response-Time', `${ms}ms`);
|
||||||
|
};
|
||||||
18
src/strategies/LocalStrategy.ts
Normal file
18
src/strategies/LocalStrategy.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { Context } from 'koa';
|
||||||
|
|
||||||
|
import { STRATEGIES } from '@mifi/auth-common/lib/enums/strategies';
|
||||||
|
import { authenticate } from '@mifi/auth-db/lib/api/authenticate';
|
||||||
|
import { Strategy } from '@mifi/auth-gatekeeper/lib';
|
||||||
|
import { GatekeeperUser } from '@mifi/auth-gatekeeper/lib/types/GatekeeperUser';
|
||||||
|
|
||||||
|
interface LocalStrategyBody {
|
||||||
|
username: string;
|
||||||
|
password: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const name = STRATEGIES[STRATEGIES.LOCAL];
|
||||||
|
|
||||||
|
export const LocalStrategy = new Strategy(name, async (ctx: Context) => {
|
||||||
|
const { password, username } = <LocalStrategyBody>ctx.request.body;
|
||||||
|
return authenticate(username, password) as Promise<false | GatekeeperUser>;
|
||||||
|
});
|
||||||
5
src/types/PasskeyUser.ts
Normal file
5
src/types/PasskeyUser.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
export interface PasskeyUser {
|
||||||
|
displayName: string;
|
||||||
|
id: Buffer | string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { RESET_ROUTE, ROUTE_PREFIX, VERIFICATION_ROUTE } from '../constants/env';
|
import { RESET_ROUTE, ROUTE_PREFIX, VERIFICATION_ROUTE } from '@mifi/auth-common/lib/env/routes';
|
||||||
|
|
||||||
export const getPasswordResetPath = (token: string) => `${ROUTE_PREFIX}${RESET_ROUTE}?t=${token}`;
|
export const getPasswordResetPath = (token: string) => `${ROUTE_PREFIX}${RESET_ROUTE}?t=${token}`;
|
||||||
|
|
||||||
@@ -2,11 +2,16 @@
|
|||||||
"extends": "@tsconfig/node16/tsconfig.json",
|
"extends": "@tsconfig/node16/tsconfig.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"allowSyntheticDefaultImports": true,
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"declaration": true,
|
||||||
"experimentalDecorators": true,
|
"experimentalDecorators": true,
|
||||||
"emitDecoratorMetadata": true,
|
"emitDecoratorMetadata": true,
|
||||||
"noImplicitAny": true,
|
"noImplicitAny": true,
|
||||||
"outDir": "./dist/",
|
"outDir": "dist/",
|
||||||
"rootDirs": ["lib"],
|
"rootDirs": ["./", "src/"],
|
||||||
"sourceMap": true
|
"strict": true,
|
||||||
}
|
"esModuleInterop": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"removeComments": false
|
||||||
|
},
|
||||||
|
"include": ["src"]
|
||||||
}
|
}
|
||||||
|
|||||||
17
tsconfig.package.json
Normal file
17
tsconfig.package.json
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"extends": "@tsconfig/node16/tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"declaration": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"emitDecoratorMetadata": true,
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"outDir": "dist/",
|
||||||
|
"rootDirs": ["./", "src/"],
|
||||||
|
"strict": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"removeComments": false
|
||||||
|
},
|
||||||
|
"include": ["./src"]
|
||||||
|
}
|
||||||
17
tsconfig.production.json
Normal file
17
tsconfig.production.json
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"extends": "@tsconfig/node16/tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"declaration": false,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"emitDecoratorMetadata": true,
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"outDir": "dist/",
|
||||||
|
"rootDirs": ["./", "src/"],
|
||||||
|
"strict": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"sourceMap": false,
|
||||||
|
"removeComments": true
|
||||||
|
},
|
||||||
|
"include": ["./src"]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user