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)
57 local := a.localhostAuthn(req)
59 ctx = newContextWithLocalhost(ctx)
62 if !local && strings.HasPrefix(req.URL.Path, "/backup-wallet") {
63 return req.WithContext(ctx), errors.New("only local can get access backup-wallets")
66 if !local && strings.HasPrefix(req.URL.Path, "/restore-wallet") {
67 return req.WithContext(ctx), errors.New("only local can get access restore-wallet")
70 if !local && strings.HasPrefix(req.URL.Path, "/list-access-tokens") {
71 return req.WithContext(ctx), errors.New("only local can get access token list")
74 // Temporary workaround. Dashboard is always ok.
75 // See loopbackOn comment above.
76 if strings.HasPrefix(req.URL.Path, "/dashboard/") || req.URL.Path == "/dashboard" {
77 return req.WithContext(ctx), nil
79 // Adding this workaround for Equity Playground.
80 if strings.HasPrefix(req.URL.Path, "/equity/") || req.URL.Path == "/equity" {
81 return req.WithContext(ctx), nil
83 if loopbackOn && local {
84 return req.WithContext(ctx), nil
87 return req.WithContext(ctx), err
90 // returns true if this request is coming from a loopback address
91 func (a *API) localhostAuthn(req *http.Request) bool {
92 h, _, err := net.SplitHostPort(req.RemoteAddr)
96 if !net.ParseIP(h).IsLoopback() {
102 func (a *API) tokenAuthn(req *http.Request) (string, error) {
107 user, pw, ok := req.BasicAuth()
109 return "", ErrNoToken
111 return user, a.cachedTokenAuthnCheck(req.Context(), user, pw)
114 func (a *API) cachedTokenAuthnCheck(ctx context.Context, user, pw string) error {
116 res, ok := a.tokenMap[user+pw]
118 if !ok || time.Now().After(res.lastLookup.Add(tokenExpiry)) {
119 err := a.tokens.Check(user, pw)
121 return ErrInvalidToken
123 res = tokenResult{lastLookup: time.Now()}
125 a.tokenMap[user+pw] = res