Skip to content

Commit

Permalink
feat: Add "authenticated" user roles and permissions, and amend API d…
Browse files Browse the repository at this point in the history
…efinitions [DEV-2684] (#267)

* THe first variant of API auth implementation

* Refactoring + chain of responsibility for auth

* Modifications:
- Injected chain of responsibilities for auth
- Renamed `revocation` API endpoint to `credential-status`
- Fixed logic when authentication enabled or not.
- small refactoring
  • Loading branch information
Andrew Nikitin authored Jun 26, 2023
1 parent 44c397e commit 2a6b30f
Show file tree
Hide file tree
Showing 21 changed files with 796 additions and 177 deletions.
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,15 @@ We use a self-hosted version of [LogTo](https://logto.io/), which supports OpenI
1. `ENABLE_AUTHENTICATION`: Turns API authentication guards on/off (Default: `false`). If `ENABLE_AUTHENTICATION=false`, then define below environment variable in `.env` file:
- `DEFAULT_CUSTOMER_ID`: Customer/user in LogTo to use for unauthenticated users.
2. `LOGTO_ENDPOINT`: API endpoint for LogTo server
3. `LOGTO_RESOURCE_URL`: API resource associated with application
3. `LOGTO_DEFAULT_RESOURCE_URL`: Usually it will be a root of all API resources. All the resourceAPI will be constructed on top of that.
4. `LOGTO_APP_ID`: Application ID from LogTo. For now, Application is supposed to be a TraditionalWeb
5. `LOGTO_APP_SECRET`: Application secret. Also should encrypted in deployment
6. `ALL_SCOPES`: List of all scopes. Should be a string with scopes divided by whitespace, like `account:create account:read did:create`
7. `COOKIE_SECRET`: Secret for cookie encryption.
6. `LOGTO_M2M_APP_ID`: Machine-to-machine Application ID
7. `LOGTO_M2M_APP_SECRET`: Machine-to-machine Application secret
8. `LOGTO_MANAGEMENT_API`: URL of management API for LogTo (default is `https://default.logto.app/api`)
9. `ALLOWED_ORIGINS`: CORS allowed origins used in the app
10. `DEFAULT_CUSTOMER_ID`: Customer/user in LogTo to use for unauthenticated users
11. `COOKIE_SECRET`: Secret for cookie encryption.

### 3rd Party Connectors

Expand Down
12 changes: 8 additions & 4 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,15 @@ ARG EXTERNAL_DB_CERT
# LogTo: build-time
ARG ENABLE_AUTHENTICATION=false
ARG LOGTO_ENDPOINT
ARG LOGTO_RESOURCE_URL
ARG LOGTO_DEFAULT_RESOURCE_URL
ARG LOGTO_APP_ID
ARG LOGTO_APP_SECRET
ARG ALLOWED_ORIGINS
ARG DEFAULT_CUSTOMER_ID
ARG ALL_SCOPES
ARG COOKIE_SECRET
ARG LOGTO_M2M_APP_ID
ARG LOGTO_M2M_APP_SECRET
ARG LOGTO_MANAGEMENT_API

# Verida connector: build-time
ARG ENABLE_VERIDA_CONNECTOR=false
Expand Down Expand Up @@ -93,12 +95,14 @@ ENV EXTERNAL_DB_CERT ${EXTERNAL_DB_CERT}
ENV ENABLE_AUTHENTICATION ${ENABLE_AUTHENTICATION}
ENV DEFAULT_CUSTOMER_ID ${DEFAULT_CUSTOMER_ID}
ENV LOGTO_ENDPOINT ${LOGTO_ENDPOINT}
ENV LOGTO_RESOURCE_URL ${LOGTO_RESOURCE_URL}
ENV LOGTO_DEFAULT_RESOURCE_URL ${LOGTO_DEFAULT_RESOURCE_URL}
ENV LOGTO_APP_ID ${LOGTO_APP_ID}
ENV LOGTO_APP_SECRET ${LOGTO_APP_SECRET}
ENV ALLOWED_ORIGINS ${ALLOWED_ORIGINS}
ENV ALL_SCOPES ${ALL_SCOPES}
ENV COOKIE_SECRET ${COOKIE_SECRET}
ENV LOGTO_M2M_APP_ID ${LOGTO_M2M_APP_ID}
ENV LOGTO_M2M_APP_SECRET ${LOGTO_M2M_APP_SECRET}
ENV LOGTO_MANAGEMENT_API ${LOGTO_MANAGEMENT_API}

# Environment variables: Verida connector
ENV ENABLE_VERIDA_CONNECTOR ${ENABLE_VERIDA_CONNECTOR}
Expand Down
7 changes: 4 additions & 3 deletions example.env
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,20 @@ EXTERNAL_DB_ENCRYPTION_KEY="<db-encryption-key>"
EXTERNAL_DB_CERT="<path/to/extenal-db-cert>"

# OpenId
LOGTO_RESOURCE_URL='http://localhost:8787'
LOGTO_DEFAULT_RESOURCE_URL='http://localhost:8787'

# LogTo
LOGTO_ENDPOINT='http://localhost:3001'
LOGTO_APP_ID='ldfsr...rq432'
LOGTO_APP_SECRET='sdf...sdf'
LOGTO_M2M_APP_ID="aaaa.....ddddd"
LOGTO_M2M_APP_SECRET="aaaa.....ddddd"
LOGTO_MANAGEMENT_API="https://default.logto.app/api"
COOKIE_SECRET='sdf...sdf'

# Authentication
ENABLE_AUTHENTICATION="false"
DEFAULT_CUSTOMER_ID="default customer id"
ALL_SCOPES="account:create did:create credential:issue"


# verida
ENABLE_VERIDA_CONNECTOR="string,default:false"
Expand Down
24 changes: 18 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
"express-session": "^1.17.3",
"express-validator": "^7.0.1",
"helmet": "^7.0.0",
"json-stringify-safe": "^5.0.1",
"node-cache": "^5.1.2",
"pg": "^8.11.0",
"pg-connection-string": "^2.6.0",
Expand All @@ -90,6 +91,7 @@
"@types/express": "^4.17.17",
"@types/express-session": "^1.17.7",
"@types/helmet": "^4.0.0",
"@types/json-stringify-safe": "^5.0.0",
"@types/node": "^20.2.5",
"@types/secp256k1": "^4.0.3",
"@types/swagger-ui-express": "^4.1.3",
Expand Down
19 changes: 11 additions & 8 deletions src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,11 @@ dotenv.config()
import { UserInfo } from './controllers/user_info.js'
import path from 'path'

const swagger_options = {
customJs: '/static/custom-button.js',
let swagger_options = {}
if (process.env.ENABLE_AUTHENTICATION === 'true') {
swagger_options = {
customJs: '/static/custom-button.js',
}
}

class App {
Expand Down Expand Up @@ -55,9 +58,10 @@ class App {
this.express.use(cookieParser())
if (process.env.ENABLE_AUTHENTICATION === 'true') {
this.express.use(session({secret: process.env.COOKIE_SECRET, cookie: { maxAge: 14 * 24 * 60 * 60 }}))
this.express.use(Authentication.withLogtoWrapper)
this.express.use(Authentication.guard)
this.express.use(Authentication.wrapperHandleAuthRoutes)
}
this.express.use(handleAuthRoutes(configLogToExpress))
this.express.use(withLogto(configLogToExpress))
this.express.use(express.text())

this.express.use(
Expand All @@ -67,7 +71,6 @@ class App {
return res.send(swaggerUi.generateHTML(swaggerJSONDoc, swagger_options))
}
)
this.express.use(Authentication.guard)
this.express.use(Authentication.handleError)
this.express.use(Authentication.accessControl)
}
Expand All @@ -85,9 +88,9 @@ class App {
app.post('/credential/suspend', new CredentialController().suspend)
app.post('/credential/reinstate', new CredentialController().reinstate)

//revocation
app.post('/revocation/statusList2021/create', RevocationController.didValidator, RevocationController.statusListValidator, new RevocationController().createStatusList)
app.get('/revocation/statusList2021/list', RevocationController.didValidator, new RevocationController().fetchStatusList)
//credential-status
app.post('/credential-status/statusList2021/create', RevocationController.didValidator, RevocationController.statusListValidator, new RevocationController().createStatusList)
app.get('/credential-status/statusList2021/list', RevocationController.didValidator, new RevocationController().fetchStatusList)
// store
app.post(`/store`, new StoreController().set)
app.get(`/store/:id`, new StoreController().get)
Expand Down
2 changes: 1 addition & 1 deletion src/controllers/user_info.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Request, Response, NextFunction, json } from 'express'
import { Request, Response } from 'express'

import * as dotenv from 'dotenv'
dotenv.config()
Expand Down
19 changes: 19 additions & 0 deletions src/middleware/auth/account_auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Request, Response } from "express";
import { AbstractAuthHandler } from "./base_auth.js";
import { IAuthResponse } from "../../types/authentication.js";

export class AccountAuthHandler extends AbstractAuthHandler {

constructor () {
super()
this.registerRoute('/account', 'GET', 'read:account')
this.registerRoute('/account', 'POST', 'create:account')
}
public async handle(request: Request, response: Response): Promise<IAuthResponse> {
if (!request.path.includes('/account')) {
return super.handle(request, response)
}
return this.commonPermissionCheck(request)
}

}
Loading

0 comments on commit 2a6b30f

Please sign in to comment.