Skip to content

Commit

Permalink
Protect create2 verification with an auth0 authentication (#1090)
Browse files Browse the repository at this point in the history
* generalize auth0 authentication functions
* support different environments via env
* support in ui docker build and update env
* auth0: in ui instead of refreshing the token every 25 seconds, refresh the token each new request
* remove old token management
* Improve UI
* add tests for create2 auth0 authentication
* auth0: add missing AUTH0_CLIENTID to dev and stable envs
* solve linter error
* auth0: fix for linter
* forbidden error for apiCheckPermission
* accept only salt when is convertible to BigNumber
* handle bearer tokens in create2 routes
* check the user's information to validate the user instead of relying on short living tokens
* fix missing getAccessTokenSilently in useEffect's dependencies
* add missing openid, profile scopes in auth0 in tests
  • Loading branch information
marcocastignoli authored Jul 24, 2023
1 parent e4fbfd4 commit c1705ea
Show file tree
Hide file tree
Showing 27 changed files with 523 additions and 187 deletions.
8 changes: 7 additions & 1 deletion environments/.env.dev
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,10 @@ GRAFANA_EXTERNAL_PORT=3000
GRAFANA_LOKI_EXTERNAL_PORT=3100
GRAFANA_PROMETHEUS_EXTERNAL_PORT=9090
# Use if you'll have a running Loki instance, otherwise leave blank
# GRAFANA_LOKI_URL=http://localhost:3100
# GRAFANA_LOKI_URL=http://localhost:3100

# Authentication
AUTH0_AUDIENCE=https://sourcify.dev
AUTH0_ISSUERBASEURL=https://dev-htkreq1l71u1hn5l.us.auth0.com/
AUTH0_TOKENSIGNINGALG=RS256
AUTH0_CLIENTID=epipuQWJL67dVggPvxNmAy40ggzNum9F
8 changes: 7 additions & 1 deletion environments/.env.latest
Original file line number Diff line number Diff line change
Expand Up @@ -91,4 +91,10 @@ GRAFANA_HTTP_PASS=xxx
GRAFANA_EXTERNAL_PORT=13000
GRAFANA_LOKI_EXTERNAL_PORT=13100
GRAFANA_PROMETHEUS_EXTERNAL_PORT=9090
GRAFANA_LOKI_URL=http://grafana-loki-latest:3100
GRAFANA_LOKI_URL=http://grafana-loki-latest:3100

# Authentication
AUTH0_AUDIENCE=https://staging.sourcify.dev/
AUTH0_ISSUERBASEURL=https://sourcify-staging.eu.auth0.com/
AUTH0_TOKENSIGNINGALG=RS256
AUTH0_CLIENTID=BUtkg8OwkVgfbzyUDH0YlD1R8TO0a0nM
8 changes: 7 additions & 1 deletion environments/.env.stable
Original file line number Diff line number Diff line change
Expand Up @@ -88,4 +88,10 @@ GRAFANA_HTTP_PASS=xxx
GRAFANA_EXTERNAL_PORT=13000
GRAFANA_LOKI_EXTERNAL_PORT=13100
GRAFANA_PROMETHEUS_EXTERNAL_PORT=9090
GRAFANA_LOKI_URL=http://grafana-loki-stable:3100
GRAFANA_LOKI_URL=http://grafana-loki-stable:3100

# Authentication
AUTH0_AUDIENCE=https://sourcify.dev
AUTH0_ISSUERBASEURL=https://sourcify.eu.auth0.com
AUTH0_TOKENSIGNINGALG=RS256
AUTH0_CLIENTID=nApMV1kEehEBtDwSx1fiVmggdX57pb0Q
9 changes: 7 additions & 2 deletions openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ info:
url: https://github.com/ethereum/sourcify/blob/master/LICENSE
servers:
- url: https://sourcify.dev/server
description: Production server
description: Production server
- url: https://staging.sourcify.dev/server
description: Staging server
description: Staging server
- url: http://localhost:5555
description: Local development server address on default port 5555.
paths:
Expand Down Expand Up @@ -56,3 +56,8 @@ paths:
$ref: "src/server/controllers/repository/get-source-files-all.stateless.paths.yaml#/paths/~1files~1any~1{chain}~1{address}"
/files/{chain}/{address}:
$ref: "src/server/controllers/repository/get-source-files-full.stateless.paths.yaml#/paths/~1files~1{chain}~1{address}"
components:
securitySchemes:
BearerAuth:
type: http
scheme: bearer
51 changes: 51 additions & 0 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 @@ -60,7 +60,9 @@
"ethers": "^6.6.2",
"express": "^4.17.1",
"express-fileupload": "^1.4.0",
"express-oauth2-jwt-bearer": "^1.5.0",
"express-openapi-validator": "^5.0.4",
"express-rate-limit": "^6.7.0",
"express-session": "^1.17.1",
"http-status-codes": "^2.1.4",
"ipfs-http-client": "^56.0.3",
Expand Down
12 changes: 12 additions & 0 deletions src/common/errors/ForbiddenError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { StatusCodes } from "http-status-codes";
import { IResponseError } from "../interfaces";

export class ForbiddenError implements IResponseError {
code: number;
message: string;

constructor(message?: string) {
this.code = StatusCodes.FORBIDDEN;
this.message = message || "Forbidden";
}
}
12 changes: 12 additions & 0 deletions src/common/errors/UnauthorizedError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { StatusCodes } from "http-status-codes";
import { IResponseError } from "../interfaces";

export class UnauthorizedError implements IResponseError {
code: number;
message: string;

constructor(message?: string) {
this.code = StatusCodes.UNAUTHORIZED;
this.message = message || "Unauthorized";
}
}
7 changes: 7 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,13 @@ export default {
/^https?:\/\/(?:.+\.)?ipfs.dweb.link$/, // dweb links used by Brave browser etc.
process.env.NODE_ENV === "development" && /^https?:\/\/localhost(?::\d+)?$/, // localhost on any port
],
authentication: {
jwt: {
audience: process.env.AUTH0_AUDIENCE,
issuerBaseURL: process.env.AUTH0_ISSUERBASEURL,
tokenSigningAlg: process.env.AUTH0_TOKENSIGNINGALG || "RS256",
},
},
};

type EtherscanAPIs = {
Expand Down
10 changes: 10 additions & 0 deletions src/server/controllers/verification/create2/create2.common.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Request } from "express";
import { apiLimiter /* apiCheckPermission */ } from "../verification.common";

type Create2RequestBody = {
deployerAddress: string;
Expand Down Expand Up @@ -26,3 +27,12 @@ export interface SessionCreate2VerifyRequest extends Request {
verificationId: string;
};
}

export const apiVerifyCreate2Limiter = apiLimiter(10 * 1000, 10);

/*
export const hasVerifyCreate2Permission = apiCheckPermission(
"verify:create2",
"This user has no permission to create2 verification"
);
*/
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ openapi: "3.0.0"
paths:
/session/verify/create2:
post:
security:
- BearerAuth: []
summary: Verify create2
tags:
- Session Verification
Expand All @@ -12,9 +14,6 @@ paths:
schema:
type: object
properties:
clientToken:
type: string
description: Mandatory client token to call this api
deployerAddress:
type: string
format: address
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,22 @@ import {
sessionVerifyCreate2,
sessionPrecompileContract,
} from "./create2.session.handlers";
import { authenticatedRequest } from "../../verification.common";
import { safeHandler } from "../../../controllers.common";
import { isAuth0EnabledUser, jwtCheck } from "../../verification.common";
import {
// hasVerifyCreate2Permission,
apiVerifyCreate2Limiter,
} from "../create2.common";

const router: Router = Router();

router
.route("/session/verify/create2")
.post(authenticatedRequest, safeHandler(sessionVerifyCreate2));
router.route("/session/verify/create2").post(
jwtCheck,
// hasVerifyCreate2Permission,
isAuth0EnabledUser,
apiVerifyCreate2Limiter,
safeHandler(sessionVerifyCreate2)
);

router
.route(["/session/verify/create2/compile"])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,17 @@ openapi: "3.0.0"
paths:
/verify/create2:
post:
summary: Verify create2
summary: Verify create2 (requires authentication)
tags:
- Stateless Verification
security:
- BearerAuth: []
requestBody:
content:
application/json:
schema:
type: object
properties:
clientToken:
type: string
description: Mandatory client token to call this api
example: ""
deployerAddress:
type: string
format: address
Expand Down Expand Up @@ -146,3 +144,19 @@ paths:
Deployed and recompiled mismatch:
value:
error: "The deployed and recompiled bytecode don't match."
"401":
description: Unauthorized
content:
application/json:
schema:
type: object
properties:
error:
type: string
examples:
Authorization header required:
value:
message: "Authorization header required"
Bad Formatted Json:
value:
error: "Unexpected token ' in JSON at position 107"
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
import { Router } from "express";
import { verifyCreate2Handler } from "./create2.stateless.handlers";
import { authenticatedRequest } from "../../verification.common";
import { safeHandler } from "../../../controllers.common";
import { isAuth0EnabledUser, jwtCheck } from "../../verification.common";
import {
// hasVerifyCreate2Permission,
apiVerifyCreate2Limiter,
} from "../create2.common";

const router: Router = Router();

router
.route("/verify/create2")
.post(authenticatedRequest, safeHandler(verifyCreate2Handler));
router.route("/verify/create2").post(
jwtCheck,
// hasVerifyCreate2Permission,
isAuth0EnabledUser,
apiVerifyCreate2Limiter,
safeHandler(verifyCreate2Handler)
);

export default router;
Loading

0 comments on commit c1705ea

Please sign in to comment.