forked from stellar/go
/
token.go
141 lines (123 loc) · 3.84 KB
/
token.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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
package serve
import (
"net/http"
"strings"
"time"
"github.com/stellar/go/clients/horizonclient"
"github.com/stellar/go/keypair"
"github.com/stellar/go/support/http/httpdecode"
supportlog "github.com/stellar/go/support/log"
"github.com/stellar/go/support/render/httpjson"
"github.com/stellar/go/txnbuild"
"gopkg.in/square/go-jose.v2"
"gopkg.in/square/go-jose.v2/jwt"
)
type tokenHandler struct {
Logger *supportlog.Entry
HorizonClient horizonclient.ClientInterface
NetworkPassphrase string
SigningAddress *keypair.FromAddress
JWK jose.JSONWebKey
JWTIssuer string
JWTExpiresIn time.Duration
AllowAccountsThatDoNotExist bool
}
type tokenRequest struct {
Transaction string `json:"transaction" form:"transaction"`
}
type tokenResponse struct {
Token string `json:"token"`
}
func (h tokenHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
req := tokenRequest{}
err := httpdecode.Decode(r, &req)
if err != nil {
badRequest.Render(w)
return
}
tx, clientAccountID, err := txnbuild.ReadChallengeTx(req.Transaction, h.SigningAddress.Address(), h.NetworkPassphrase)
if err != nil {
badRequest.Render(w)
return
}
hash, err := tx.HashHex(h.NetworkPassphrase)
if err != nil {
h.Logger.Ctx(ctx).WithStack(err).Error(err)
serverError.Render(w)
return
}
l := h.Logger.Ctx(ctx).
WithField("tx", hash).
WithField("account", clientAccountID)
l.Info("Start verifying challenge transaction.")
var clientAccountExists bool
clientAccount, err := h.HorizonClient.AccountDetail(horizonclient.AccountRequest{AccountID: clientAccountID})
switch {
case err == nil:
clientAccountExists = true
l.Infof("Account exists.")
case horizonclient.IsNotFoundError(err):
clientAccountExists = false
l.Infof("Account does not exist.")
default:
l.WithStack(err).Error(err)
serverError.Render(w)
return
}
var signersVerified []string
if clientAccountExists {
requiredThreshold := txnbuild.Threshold(clientAccount.Thresholds.HighThreshold)
clientSignerSummary := clientAccount.SignerSummary()
signersVerified, err = txnbuild.VerifyChallengeTxThreshold(req.Transaction, h.SigningAddress.Address(), h.NetworkPassphrase, requiredThreshold, clientSignerSummary)
if err != nil {
l.
WithField("signersCount", len(clientSignerSummary)).
WithField("signaturesCount", len(tx.Signatures())).
WithField("requiredThreshold", requiredThreshold).
Info("Failed to verify with signers that do not meet threshold.")
unauthorized.Render(w)
return
}
} else {
if !h.AllowAccountsThatDoNotExist {
l.Infof("Failed to verify because accounts that do not exist are not allowed.")
unauthorized.Render(w)
return
}
signersVerified, err = txnbuild.VerifyChallengeTxSigners(req.Transaction, h.SigningAddress.Address(), h.NetworkPassphrase, clientAccountID)
if err != nil {
l.Infof("Failed to verify with account master key as signer.")
unauthorized.Render(w)
return
}
}
l.
WithField("signers", strings.Join(signersVerified, ",")).
Infof("Successfully verified challenge transaction.")
jwsOptions := &jose.SignerOptions{}
jwsOptions.WithType("JWT")
jws, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.SignatureAlgorithm(h.JWK.Algorithm), Key: h.JWK.Key}, jwsOptions)
if err != nil {
l.WithStack(err).Error(err)
serverError.Render(w)
return
}
now := time.Now().UTC()
claims := jwt.Claims{
Issuer: h.JWTIssuer,
Subject: clientAccountID,
IssuedAt: jwt.NewNumericDate(now),
Expiry: jwt.NewNumericDate(now.Add(h.JWTExpiresIn)),
}
tokenStr, err := jwt.Signed(jws).Claims(claims).CompactSerialize()
if err != nil {
l.WithStack(err).Error(err)
serverError.Render(w)
return
}
res := tokenResponse{
Token: tokenStr,
}
httpjson.Render(w, res, httpjson.JSON)
}