Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add per-ticket API authentication scheme #514

Closed

Conversation

itswisdomagain
Copy link
Member

@itswisdomagain itswisdomagain commented Sep 4, 2019

Current API routes are mounted on /api/v1/* and /api/v2/*; and authentication is carried out by providing an API access token in the Authorization request header field in the format:
Authorization: Bearer <api key>.

This PR adds a new API authentication scheme (TicketAuth) as an alternative to the current Bearer auth scheme. The added TicketAuth scheme performs per-ticket authentication before executing requested actions on protected API endpoints such as /getpurchaseinfo. It's worth mentioning that the new auth scheme works as expected with all existing API endpoints and the changes introduced in this PR do not break backwards compatibility.

The new auth scheme uses the wallet built-in message signing capability and signs a msg which content is the current timestamp, allowing old msgs to be invalidated by the server so they are resistant to replay attacks.

The format for the TicketAuth scheme is:
Authorization: TicketAuth SignedTimestamp=1564531200, Signature=frJIUN8DYpKDtOLCwo, TicketHash=591b17ed03afc916f274669939924e12ed5ac90d8cc602172fb53237b8a20522

Where;

  • TicketAuth: is the name of the authentication scheme
  • SignedTimestamp: is the unix timestamp at the time of request, i.e. the message that is signed by the wallet
  • Signature (base64 string): the result of signing the timestamp above using the private key for the ticket reward address belonging to the user's wallet (see below)
  • TicketHash: the hash of the ticket for which the requested operation is to be performed

This authorization header format matches that defined in RFC2617. See this SO answer.

User's ticket reward address

A ticket purchased with voting rights shared with a vsp has 2 sstxcommitment outputs for receiving staking reward. One of these addresses belongs to the vsp wallet, while the other belongs to the ticket owner's wallet. This second address belonging to the ticket owner is what is used in preparing the Signature passed in the authentication header as described above.

Recovering redeem scripts for restored wallets

This ticket-level authentication makes recovering redeem scripts for restored wallets much easier. Instead of logging in to the vsp server to get the redeem script for a ticket or api key for an account, a client app can simply make an API request to /getpurchaseinfo using the TicketAuth scheme to retrieve the redeem script for a ticket - provided the ticket was purchased with the vsp.

Resolves #291. Part fix for #274 and decredcommunity/issues#100.

Copy link
Member

@JoeGruffins JoeGruffins left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really have an opinion on this, but it is the policy of dcrstakepool to use naked returns as little as possible. I've marked all the naked return I saw with "Naked."

I have a few questions about this idea in general, but I want to get through #515 before I say anything.

@@ -16,6 +16,7 @@ import (
wallettypes "github.com/decred/dcrwallet/rpc/jsonrpc/types"

"github.com/decred/dcrd/rpcclient/v3"
"github.com/decred/dcrd/txscript"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be using txscript/v2

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using txscript/v2 will require using chaincfg/v2, especially for AppContext.Params (type *chaincfg.Params) which will cause issues in other existing usages of AppContext.Params.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

try /v2 again; dcrd has updated since.

string MultiSigAddress = 1;
string VspFeeAddress = 2;
string OwnerFeeAddress = 3;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might want to start tracking txids in the db, as we want to become more self-sufficient. Although I am aware that you want to reduce the communication between the db and stakepoold, because it is on another machine. A centralized area to track tickets is needed, imo. What do you think? It should be discussed somewhere... I'll make an issue after a little more thought.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure we can totally remove reliance on dcrwallet; unless that's possible and is something we're working towards, I'd prefer that where possible, we not store information that we can deduce by making 1 or 2 RPC calls. But you can open an issue for this and we can discuss it more and see what we can come up with.

Copy link
Member Author

@itswisdomagain itswisdomagain Sep 21, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I later commented below, this may be a good idea after all. I can proceed to implement in this PR if that's agreed.

backend/stakepoold/rpc/rpcserver/context.go Outdated Show resolved Hide resolved
backend/stakepoold/rpc/rpcserver/context.go Outdated Show resolved Hide resolved
backend/stakepoold/rpc/rpcserver/context.go Outdated Show resolved Hide resolved
system/ticketchallengescache.go Outdated Show resolved Hide resolved
system/ticketchallengescache.go Show resolved Hide resolved
system/ticketchallengescache.go Outdated Show resolved Hide resolved
system/ticketchallengescache.go Outdated Show resolved Hide resolved
system/ticketchallengescache.go Show resolved Hide resolved
Copy link
Member

@dajohi dajohi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rebase needed.

log.Errorf("GetTicketInfo: MsgTxFromHex failed for %v: %v", res.Hex, err)
return nil, err
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing check to verify tx is a vote.

@@ -16,6 +16,7 @@ import (
wallettypes "github.com/decred/dcrwallet/rpc/jsonrpc/types"

"github.com/decred/dcrd/rpcclient/v3"
"github.com/decred/dcrd/txscript"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

try /v2 again; dcrd has updated since.

"time"

"github.com/decred/dcrd/dcrutil"
"github.com/decred/dcrwallet/wallet"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

try /v3 again, since its already used. post compile errors here or ask on matrix.

Copy link
Member

@JoeGruffins JoeGruffins left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good to see you back at this! Left some comments.

backend/stakepoold/stakepool/stakepool.go Outdated Show resolved Hide resolved
backend/stakepoold/stakepool/stakepool.go Show resolved Hide resolved
system/api-auth.go Show resolved Hide resolved
system/ticketchallengescache.go Outdated Show resolved Hide resolved
system/core.go Outdated Show resolved Hide resolved
system/ticketchallengescache.go Show resolved Hide resolved
@dajohi dajohi modified the milestones: v1.3.0, v1.6.0 Mar 12, 2020
@JoeGruffins
Copy link
Member

JoeGruffins commented Mar 15, 2020

I get a panic after this runs for a bit

panic
2020/03/15 02:14:10 [dcrstakedinner/yfhOMEWsD7-000001] panic: runtime error: invalid memory address or nil pointer dereference
goroutine 51 [running]:
runtime/debug.Stack(0x80, 0xc000113208, 0x1)
        /usr/lib/go/src/runtime/debug/stack.go:24 +0x9d
runtime/debug.PrintStack()
        /usr/lib/go/src/runtime/debug/stack.go:16 +0x22
github.com/zenazn/goji/web/middleware.Recoverer.func1.1(0xc0001b03a0, 0x20, 0x7fd39c5596c8, 0xc000079d00)
        /home/joe/go/pkg/mod/github.com/zenazn/[email protected]/web/middleware/recoverer.go:24 +0x6f
panic(0xb3a860, 0x13715f0)
        /usr/lib/go/src/runtime/panic.go:679 +0x1b2
github.com/decred/dcrstakepool/system.(*Application).ApplyAPI.func1(0x7fd39c5596c8, 0xc000079d00, 0xc00010a200)
        /home/joe/git/dcrstakepool/system/middleware.go:74 +0x355
net/http.HandlerFunc.ServeHTTP(0xc000238d20, 0x7fd39c5596c8, 0xc000079d00, 0xc00010a200)
        /usr/lib/go/src/net/http/server.go:2007 +0x44
github.com/zenazn/goji/web.(*cStack).ServeHTTPC(...)
        /home/joe/go/pkg/mod/github.com/zenazn/[email protected]/web/middleware.go:50
github.com/zenazn/goji/web.(*Mux).ServeHTTPC(0xc000496310, 0xc000238bd0, 0xc000238c90, 0x7fd39c5596c8, 0xc000079d00, 0xc00010a200)
        /home/joe/go/pkg/mod/github.com/zenazn/[email protected]/web/mux.go:73 +0x88
github.com/zenazn/goji/web.(*router).route(0xc0004962d8, 0xc000238c00, 0x7fd39c5596c8, 0xc000079d00, 0xc00010a200)
        /home/joe/go/pkg/mod/github.com/zenazn/[email protected]/web/router.go:114 +0x5b0
github.com/zenazn/goji/web.(*mStack).newStack.func1(0x7fd39c5596c8, 0xc000079d00, 0xc00010a200)
        /home/joe/go/pkg/mod/github.com/zenazn/[email protected]/web/middleware.go:88 +0x56
net/http.HandlerFunc.ServeHTTP(0xc00051c520, 0x7fd39c5596c8, 0xc000079d00, 0xc00010a200)
        /usr/lib/go/src/net/http/server.go:2007 +0x44
github.com/decred/dcrstakepool/system.(*Application).ApplyDbMap.func1(0x7fd39c5596c8, 0xc000079d00, 0xc00010a200)
        /home/joe/git/dcrstakepool/system/middleware.go:42 +0xd2
net/http.HandlerFunc.ServeHTTP(0xc000238c30, 0x7fd39c5596c8, 0xc000079d00, 0xc00010a200)
        /usr/lib/go/src/net/http/server.go:2007 +0x44
github.com/zenazn/goji/web/middleware.Recoverer.func1(0x7fd39c5596c8, 0xc000079d00, 0xc00010a200)
        /home/joe/go/pkg/mod/github.com/zenazn/[email protected]/web/middleware/recoverer.go:29 +0xe1
net/http.HandlerFunc.ServeHTTP(0xc00051c680, 0x7fd39c5596c8, 0xc000079d00, 0xc00010a200)
        /usr/lib/go/src/net/http/server.go:2007 +0x44
github.com/decred/dcrstakepool/system.Logger.func1.1(0xe7cbe0, 0xc0004a0540, 0xc00010a200)
        /home/joe/git/dcrstakepool/system/middleware.go:134 +0x476
net/http.HandlerFunc.ServeHTTP(0xc000238c60, 0xe7cbe0, 0xc0004a0540, 0xc00010a200)
        /usr/lib/go/src/net/http/server.go:2007 +0x44
github.com/zenazn/goji/web/middleware.RequestID.func1(0xe7cbe0, 0xc0004a0540, 0xc00010a200)
        /home/joe/go/pkg/mod/github.com/zenazn/[email protected]/web/middleware/request_id.go:68 +0x1d7
net/http.HandlerFunc.ServeHTTP(0xc00051c6a0, 0xe7cbe0, 0xc0004a0540, 0xc00010a200)
        /usr/lib/go/src/net/http/server.go:2007 +0x44
github.com/zenazn/goji/web.(*cStack).ServeHTTPC(...)
        /home/joe/go/pkg/mod/github.com/zenazn/[email protected]/web/middleware.go:50
github.com/zenazn/goji/web.(*Mux).ServeHTTPC(0xc0004962a0, 0xc000238bd0, 0x0, 0xe7cbe0, 0xc0004a0540, 0xc00010a200)
        /home/joe/go/pkg/mod/github.com/zenazn/[email protected]/web/mux.go:73 +0x88
github.com/zenazn/goji/web.(*router).route(0xc000496d58, 0xc000238a80, 0xe7cbe0, 0xc0004a0540, 0xc00010a200)
        /home/joe/go/pkg/mod/github.com/zenazn/[email protected]/web/router.go:114 +0x5b0
github.com/zenazn/goji/web.(*mStack).newStack.func1(0xe7cbe0, 0xc0004a0540, 0xc00010a200)
        /home/joe/go/pkg/mod/github.com/zenazn/[email protected]/web/middleware.go:88 +0x56
net/http.HandlerFunc.ServeHTTP(0xc00051c080, 0xe7cbe0, 0xc0004a0540, 0xc00010a200)
        /usr/lib/go/src/net/http/server.go:2007 +0x44
github.com/zenazn/goji/web.(*cStack).ServeHTTP(...)
        /home/joe/go/pkg/mod/github.com/zenazn/[email protected]/web/middleware.go:46
github.com/zenazn/goji/web.(*Mux).ServeHTTP(0xc000496d20, 0xe7cbe0, 0xc0004a0540, 0xc00010a200)
        /home/joe/go/pkg/mod/github.com/zenazn/[email protected]/web/mux.go:65 +0x7d
net/http.serverHandler.ServeHTTP(0xc0004a0620, 0xe7cbe0, 0xc0004a0540, 0xc00010a200)
        /usr/lib/go/src/net/http/server.go:2802 +0xa4
net/http.(*conn).serve(0xc000506fa0, 0xe7ec20, 0xc000079540)
        /usr/lib/go/src/net/http/server.go:1890 +0x875
created by net/http.(*Server).Serve
        /usr/lib/go/src/net/http/server.go:2927 +0x38e
2020/03/15 02:14:10 [dcrstakedinner/yfhOMEWsD7-000002] panic: runtime error: invalid memory address or nil pointer dereference
goroutine 53 [running]:
runtime/debug.Stack(0x80, 0xc000113208, 0x1)
        /usr/lib/go/src/runtime/debug/stack.go:24 +0x9d
runtime/debug.PrintStack()
        /usr/lib/go/src/runtime/debug/stack.go:16 +0x22
github.com/zenazn/goji/web/middleware.Recoverer.func1.1(0xc0001b0440, 0x20, 0x7fd39c5596c8, 0xc00016e300)
        /home/joe/go/pkg/mod/github.com/zenazn/[email protected]/web/middleware/recoverer.go:24 +0x6f
panic(0xb3a860, 0x13715f0)
        /usr/lib/go/src/runtime/panic.go:679 +0x1b2
github.com/decred/dcrstakepool/system.(*Application).ApplyAPI.func1(0x7fd39c5596c8, 0xc00016e300, 0xc00010a300)
        /home/joe/git/dcrstakepool/system/middleware.go:74 +0x355
net/http.HandlerFunc.ServeHTTP(0xc000238ff0, 0x7fd39c5596c8, 0xc00016e300, 0xc00010a300)
        /usr/lib/go/src/net/http/server.go:2007 +0x44
github.com/zenazn/goji/web.(*cStack).ServeHTTPC(...)
        /home/joe/go/pkg/mod/github.com/zenazn/[email protected]/web/middleware.go:50
github.com/zenazn/goji/web.(*Mux).ServeHTTPC(0xc000496310, 0xc000238f30, 0xc000238f60, 0x7fd39c5596c8, 0xc00016e300, 0xc00010a300)
        /home/joe/go/pkg/mod/github.com/zenazn/[email protected]/web/mux.go:73 +0x88
github.com/zenazn/goji/web.(*router).route(0xc0004962d8, 0xc000238c00, 0x7fd39c5596c8, 0xc00016e300, 0xc00010a300)
        /home/joe/go/pkg/mod/github.com/zenazn/[email protected]/web/router.go:114 +0x5b0
github.com/zenazn/goji/web.(*mStack).newStack.func1(0x7fd39c5596c8, 0xc00016e300, 0xc00010a300)
        /home/joe/go/pkg/mod/github.com/zenazn/[email protected]/web/middleware.go:88 +0x56
net/http.HandlerFunc.ServeHTTP(0xc00051c520, 0x7fd39c5596c8, 0xc00016e300, 0xc00010a300)
        /usr/lib/go/src/net/http/server.go:2007 +0x44
github.com/decred/dcrstakepool/system.(*Application).ApplyDbMap.func1(0x7fd39c5596c8, 0xc00016e300, 0xc00010a300)
        /home/joe/git/dcrstakepool/system/middleware.go:42 +0xd2
net/http.HandlerFunc.ServeHTTP(0xc000238c30, 0x7fd39c5596c8, 0xc00016e300, 0xc00010a300)
        /usr/lib/go/src/net/http/server.go:2007 +0x44
github.com/zenazn/goji/web/middleware.Recoverer.func1(0x7fd39c5596c8, 0xc00016e300, 0xc00010a300)
        /home/joe/go/pkg/mod/github.com/zenazn/[email protected]/web/middleware/recoverer.go:29 +0xe1
net/http.HandlerFunc.ServeHTTP(0xc00051c680, 0x7fd39c5596c8, 0xc00016e300, 0xc00010a300)
        /usr/lib/go/src/net/http/server.go:2007 +0x44
github.com/decred/dcrstakepool/system.Logger.func1.1(0xe7cbe0, 0xc0004a0700, 0xc00010a300)
        /home/joe/git/dcrstakepool/system/middleware.go:134 +0x476
net/http.HandlerFunc.ServeHTTP(0xc000238c60, 0xe7cbe0, 0xc0004a0700, 0xc00010a300)
        /usr/lib/go/src/net/http/server.go:2007 +0x44
github.com/zenazn/goji/web/middleware.RequestID.func1(0xe7cbe0, 0xc0004a0700, 0xc00010a300)
        /home/joe/go/pkg/mod/github.com/zenazn/[email protected]/web/middleware/request_id.go:68 +0x1d7
net/http.HandlerFunc.ServeHTTP(0xc00051c6a0, 0xe7cbe0, 0xc0004a0700, 0xc00010a300)
        /usr/lib/go/src/net/http/server.go:2007 +0x44
github.com/zenazn/goji/web.(*cStack).ServeHTTPC(...)
        /home/joe/go/pkg/mod/github.com/zenazn/[email protected]/web/middleware.go:50
github.com/zenazn/goji/web.(*Mux).ServeHTTPC(0xc0004962a0, 0xc000238f30, 0x0, 0xe7cbe0, 0xc0004a0700, 0xc00010a300)
        /home/joe/go/pkg/mod/github.com/zenazn/[email protected]/web/mux.go:73 +0x88
github.com/zenazn/goji/web.(*router).route(0xc000496d58, 0xc000238a80, 0xe7cbe0, 0xc0004a0700, 0xc00010a300)
        /home/joe/go/pkg/mod/github.com/zenazn/[email protected]/web/router.go:114 +0x5b0
github.com/zenazn/goji/web.(*mStack).newStack.func1(0xe7cbe0, 0xc0004a0700, 0xc00010a300)
        /home/joe/go/pkg/mod/github.com/zenazn/[email protected]/web/middleware.go:88 +0x56
net/http.HandlerFunc.ServeHTTP(0xc00051c080, 0xe7cbe0, 0xc0004a0700, 0xc00010a300)
        /usr/lib/go/src/net/http/server.go:2007 +0x44
github.com/zenazn/goji/web.(*cStack).ServeHTTP(...)
        /home/joe/go/pkg/mod/github.com/zenazn/[email protected]/web/middleware.go:46
github.com/zenazn/goji/web.(*Mux).ServeHTTP(0xc000496d20, 0xe7cbe0, 0xc0004a0700, 0xc00010a300)
        /home/joe/go/pkg/mod/github.com/zenazn/[email protected]/web/mux.go:65 +0x7d
net/http.serverHandler.ServeHTTP(0xc0004a0620, 0xe7cbe0, 0xc0004a0700, 0xc00010a300)
        /usr/lib/go/src/net/http/server.go:2802 +0xa4
net/http.(*conn).serve(0xc000507040, 0xe7ec20, 0xc000079d40)
        /usr/lib/go/src/net/http/server.go:1890 +0x875
created by net/http.(*Server).Serve
        /usr/lib/go/src/net/http/server.go:2927 +0x38e

@JoeGruffins
Copy link
Member

I created a consumer for this api here: decred/tinydecred#121

system/middleware.go Outdated Show resolved Hide resolved
Copy link
Member

@JoeGruffins JoeGruffins left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me!

Works great. Is versatile and clients can choose to use it or not. I can't find any holes in the model.

If you could just up the stakepoold api version major to 10, I will approve.

@itswisdomagain
Copy link
Member Author

Looks good to me!

Thanks for the review, mate.

If you could just up the stakepoold api version major to 10, I will approve.

Thinking a minor version bump will suffice? Since there are no backwards-incompatible changes introduced, just new functionality that is backward-compatible.

@JoeGruffins
Copy link
Member

Thinking a minor version bump will suffice? Since there are no backwards-incompatible changes introduced, just new functionality that is backward-compatible.

Yeah? well ok. It does add a function to stakepoold that will cause dcrstakepoold to fail if it can't get a response, but if it's not something anyone is using yet, I suppose minor is fine. The more times I read the semver suggested usage the more I doubt myself.

Minor is fine with me.

@itswisdomagain
Copy link
Member Author

It does add a function to stakepoold that will cause dcrstakepoold to fail if it can't get a response

Yeah... will also bump the dcrstakepool requiredStakepooldAPI version to match, so a user is unable to run dcrstakepool with an incompatible stakepoold version.

@itswisdomagain
Copy link
Member Author

itswisdomagain commented May 10, 2020

Closed in favour of #625

@itswisdomagain itswisdomagain mentioned this pull request May 15, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Pubkey based authentication
4 participants