11 "github.com/vapor/accesstoken"
12 "github.com/vapor/errors"
15 const tokenExpiry = time.Minute * 5
20 //ErrInvalidToken is returned when authenticate is called with invalid token.
21 ErrInvalidToken = errors.New("invalid token")
22 //ErrNoToken is returned when authenticate is called with no token.
23 ErrNoToken = errors.New("no token")
26 //API describe the token authenticate.
29 tokens *accesstoken.CredentialStore
30 tokenMu sync.Mutex // protects the following
31 tokenMap map[string]tokenResult
34 type tokenResult struct {
38 //NewAPI create a token authenticate object.
39 func NewAPI(tokens *accesstoken.CredentialStore, disable bool) *API {
43 tokenMap: make(map[string]tokenResult),
47 // Authenticate returns the request, with added tokens and/or localhost
48 // flags in the context, as appropriate.
49 func (a *API) Authenticate(req *http.Request) (*http.Request, error) {
52 token, err := a.tokenAuthn(req)
53 if err == nil && token != "" {
54 // if this request was successfully authenticated with a token, pass the token along
55 ctx = newContextWithToken(ctx, token)
58 local := a.localhostAuthn(req)
60 ctx = newContextWithLocalhost(ctx)
63 if !local && strings.HasPrefix(req.URL.Path, "/backup-wallet") {
64 return req.WithContext(ctx), errors.New("only local can get access backup-wallets")
67 if !local && strings.HasPrefix(req.URL.Path, "/restore-wallet") {
68 return req.WithContext(ctx), errors.New("only local can get access restore-wallet")
71 if !local && strings.HasPrefix(req.URL.Path, "/list-access-tokens") {
72 return req.WithContext(ctx), errors.New("only local can get access token list")
75 // Temporary workaround. Dashboard is always ok.
76 // See loopbackOn comment above.
77 if strings.HasPrefix(req.URL.Path, "/dashboard/") || req.URL.Path == "/dashboard" {
78 return req.WithContext(ctx), nil
80 // Adding this workaround for Equity Playground.
81 if strings.HasPrefix(req.URL.Path, "/equity/") || req.URL.Path == "/equity" {
82 return req.WithContext(ctx), nil
84 if loopbackOn && local {
85 return req.WithContext(ctx), nil
88 return req.WithContext(ctx), err
91 // returns true if this request is coming from a loopback address
92 func (a *API) localhostAuthn(req *http.Request) bool {
93 h, _, err := net.SplitHostPort(req.RemoteAddr)
97 if !net.ParseIP(h).IsLoopback() {
103 func (a *API) tokenAuthn(req *http.Request) (string, error) {
108 user, pw, ok := req.BasicAuth()
110 return "", ErrNoToken
112 return user, a.cachedTokenAuthnCheck(req.Context(), user, pw)
115 func (a *API) cachedTokenAuthnCheck(ctx context.Context, user, pw string) error {
117 res, ok := a.tokenMap[user+pw]
119 if !ok || time.Now().After(res.lastLookup.Add(tokenExpiry)) {
120 err := a.tokens.Check(user, pw)
122 return ErrInvalidToken
124 res = tokenResult{lastLookup: time.Now()}
126 a.tokenMap[user+pw] = res