Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: allow injecting an authorization middleware #6

Merged
merged 1 commit into from
May 29, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 61 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,73 @@ Kill the process with the PID specified in `$PWD/cors-proxy.pid`:
cors-proxy stop
```

## Configuration
### CLI configuration

Environment variables:
- `PORT` the port to listen to (if run with `npm start`)
- `ALLOW_ORIGIN` the value for the 'Access-Control-Allow-Origin' CORS header
- `INSECURE_HTTP_ORIGINS` comma separated list of origins for which HTTP should be used instead of HTTPS (added to make developing against locally running git servers easier)


## Middleware usage

You can also use the `cors-proxy` as a middleware in your own server.

```js
const express = require('express')
const corsProxy = require('@isomorphic-git/cors-proxy/middleware.js')

const app = express()
const options = {}

app.use(corsProxy(options))

```

### Middleware configuration

*The middleware doesn't use the environment variables.* The options object supports the following properties:

- `origin`: _string_. The value for the 'Access-Control-Allow-Origin' CORS header
- `insecure_origins`: _string[]_. Array of origins for which HTTP should be used instead of HTTPS (added to make developing against locally running git servers easier)
- `authorization`: _(req, res, next) => void_. A middleware function you can use to handle custom authorization. Is run after filtering for git-like requests and handling CORS but before the request is proxied.

_Example:_
```ts
app.use(
corsProxy({
authorization: (req: Request, res: Response, next: NextFunction) => {
// proxied git HTTP requests already use the Authorization header for git credentials,
// so their [Company] credentials are inserted in the X-Authorization header instead.
if (getAuthorizedUser(req, 'X-Authorization')) {
return next();
} else {
return res.status(401).send("Unable to authenticate you with [Company]'s git proxy");
}
},
})
);

// Only requests with a valid JSON Web Token will be proxied
function getAuthorizedUser(req: Request, header: string = 'Authorization') {
const Authorization = req.get(header);

if (Authorization) {
const token = Authorization.replace('Bearer ', '');
try {
const verifiedToken = verify(token, env.APP_SECRET) as IToken;
if (verifiedToken) {
return {
id: verifiedToken.userId,
};
}
} catch (e) {
// noop
}
}
}
```

## License

This work is released under [The MIT License](https://opensource.org/licenses/MIT)
39 changes: 34 additions & 5 deletions middleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const allowHeaders = [
'range',
'referer',
'user-agent',
'x-authorization',
'x-http-method-override',
'x-requested-with',
]
Expand Down Expand Up @@ -58,19 +59,47 @@ const filter = (predicate, middleware) => {
return corsProxyMiddleware
}

module.exports = ({ origin, insecure_origins = [] } = {}) => {
const compose = (...handlers) => {
const composeTwo = (handler1, handler2) => {
function composed (req, res, next) {
handler1(req, res, (err) => {
if (err) {
return next(err)
} else {
return handler2(req, res, next)
}
})
}
return composed
}
let result = handlers.pop()
while(handlers.length) {
result = composeTwo(handlers.pop(), result)
}
return result
}

function noop (_req, _res, next) {
next()
}

module.exports = ({ origin, insecure_origins = [], authorization = noop } = {}) => {
function predicate (req) {
let u = url.parse(req.url, true)
// Not a git request, skip
return allow(req, u)
}
function middleware (req, res) {
let u = url.parse(req.url, true)

function sendCorsOK (req, res, next) {
// Handle CORS preflight request
if (req.method === 'OPTIONS') {
return send(res, 200, '')
} else {
next()
}
}
function middleware (req, res) {
let u = url.parse(req.url, true)


let headers = {}
for (let h of allowHeaders) {
Expand Down Expand Up @@ -110,5 +139,5 @@ module.exports = ({ origin, insecure_origins = [] } = {}) => {
allowCredentials: false,
origin
})
return filter(predicate, cors(middleware))
return filter(predicate, cors(compose(sendCorsOK, authorization, middleware)))
}