Expose local authorization http service by ngrok password protected
China is experiencing COVID-19 from the beginning of the 2020 China New Year. After the festival many programmers are working from home without heading to their office as usual for working days for safety. But there is trouble with API integration when colleagues are working without an internal network, the front-end has no access for back-end API. In this post, I will tell you guys a solution using ngrok to expose your local http service to the outside world securely.
Quick introduction to download and setup ngrok > https://ngrok.com/download
Credential for ngrok
One more thing involved ngrok setup needs to clarify is that we should expose our local service with credential for security consideration. we can run the ngrok client with -auth
. Keep in mind that it's used for ngrok authentication.
./ngrok http 3000 -auth="{username}:{password}"
When the exposed service is a webpage, access with the credential by interact way. But if the service provides web API for client, we should access the service by programing way.
Authentication on http service
Access authentication is very common on HTTP API, most of time back-end can retrieve authentication token from http request header with Authorization
key. For example on Golang, extract the Authentication on go-kit codebase https://github.com/go-kit/kit/blob/master/auth/jwt/transport.go#L22.
// HTTPToContext moves a JWT from request header to context. Particularly
// useful for servers.
func HTTPToContext() http.RequestFunc {
return func(ctx context.Context, r *stdhttp.Request) context.Context {
token, ok := extractTokenFromAuthHeader(r.Header.Get("Authorization"))
if !ok {
return ctx
}
return context.WithValue(ctx, JWTTokenContextKey, token)
}
}
Add an Auth middleware to our http service gateway:
// AuthMiddleware returns an endpoint middleware
// 添加 middleware , 减少每次 decodeXXXRequest 都要从 request header 中 extract Authorization 参数
func AuthMiddleware() endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
// Add your middleware logic here
if token, ok := ctx.Value(stdjwt.JWTTokenContextKey).(string); !ok {
// 若需要鉴权的接口请求头中不携带 Authorization jwt token, 直接给客户端返回错误,不请求鉴权服务
return nil, fmt.Errorf("Authorization Token Empty")
} else {
// verify token
}
return next(ctx, request)
}
}
}
But I found out that the Authorization
Bear in http request header from front-end would be a conflict to ngrok credential authenticates. -auth
is involved to ngrok and out of our control, but we can modify the key name Authorization
to another one.
Modify authorization
to aaauthorization
on front-end codebase, and run the proxy script, front-end connect to the proxy service.
//node proxy.js
var express = require('express');
var proxy = require('http-proxy-middleware');
var app = express();
app.use(
'',
proxy({
target: 'http://xxx.ngrok.io', // back-end service exposed by ngrok
changeOrigin: true,
xfwd: true,
onProxyReq: (proxyReq, req) => {
if (req.headers.aaauthorization != undefined && req.headers.aaauthorization != '') {
proxyReq.setHeader('x-added', req.headers.aaauthorization)
}
},
auth: '{username}:{password}'
})
);
app.listen(3009);
Finally, back-end should retrieve authorization token by x-added
key from http request header.
token, ok := extractTokenFromAuthHeader(r.Header.Get("x-added"))