-
Notifications
You must be signed in to change notification settings - Fork 280
/
rest_store.go
106 lines (96 loc) · 3.02 KB
/
rest_store.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
package sessions // import "github.com/pomerium/pomerium/internal/sessions"
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"strings"
"time"
"github.com/pomerium/pomerium/internal/cryptutil"
)
// DefaultBearerTokenHeader is default header name for the authorization bearer
// token header as defined in rfc2617
// https://tools.ietf.org/html/rfc6750#section-2.1
const DefaultBearerTokenHeader = "Authorization"
// RestStore is a session store suitable for REST
type RestStore struct {
Name string
Cipher cryptutil.Cipher
// Expire time.Duration
}
// RestStoreOptions contains the options required to build a new RestStore.
type RestStoreOptions struct {
Name string
Cipher cryptutil.Cipher
// Expire time.Duration
}
// NewRestStore creates a new RestStore from a set of RestStoreOptions.
func NewRestStore(opts *RestStoreOptions) (*RestStore, error) {
if opts.Name == "" {
opts.Name = DefaultBearerTokenHeader
}
if opts.Cipher == nil {
return nil, fmt.Errorf("internal/sessions: cipher cannot be nil")
}
return &RestStore{
Name: opts.Name,
// Expire: opts.Expire,
Cipher: opts.Cipher,
}, nil
}
// ClearSession functions differently because REST is stateless, we instead
// inform the client that this token is no longer valid.
// https://tools.ietf.org/html/rfc6750
func (s *RestStore) ClearSession(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusUnauthorized)
errMsg := `
{
"error": "invalid_token",
"token_type": "Bearer",
"error_description": "The token has expired."
}`
w.Write([]byte(errMsg))
}
// LoadSession attempts to load a pomerium session from a Bearer Token set
// in the authorization header.
func (s *RestStore) LoadSession(r *http.Request) (*SessionState, error) {
authHeader := r.Header.Get(s.Name)
split := strings.Split(authHeader, "Bearer")
if authHeader == "" || len(split) != 2 {
return nil, errors.New("internal/sessions: no bearer token header found")
}
token := strings.TrimSpace(split[1])
session, err := UnmarshalSession(token, s.Cipher)
if err != nil {
return nil, err
}
return session, nil
}
// RestStoreResponse is the JSON struct returned to the client.
type RestStoreResponse struct {
// Token is the encrypted pomerium session that can be used to
// programmatically authenticate with pomerium.
Token string
// In addition to the token, non-sensitive meta data is returned to help
// the client manage token renewals.
Expiry time.Time
}
// SaveSession returns an encrypted pomerium session as a JSON object with
// associated, non sensitive meta-data like
func (s *RestStore) SaveSession(w http.ResponseWriter, r *http.Request, sessionState *SessionState) error {
encToken, err := MarshalSession(sessionState, s.Cipher)
if err != nil {
return err
}
jsonBytes, err := json.Marshal(
&RestStoreResponse{
Token: encToken,
Expiry: sessionState.RefreshDeadline,
})
if err != nil {
return fmt.Errorf("internal/sessions: couldn't marshal token struct: %v", err)
}
w.Header().Set("Content-Type", "application/json")
w.Write(jsonBytes)
return nil
}