-
Notifications
You must be signed in to change notification settings - Fork 51
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
feat: Add authentication for ACP #2649
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, but I have a handful of questions/todos for you at the moment :)
tests/integration/acp/add_policy/with_invalid_creator_arg_test.go
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did a first pass quickly, left some todos, will review again tonight.
Can you please also update the acp readme with authentication cli examples now that we will always have acp with authentication. It will help better understand for reviewers as well, how acp with auth works.
) | ||
|
||
// GenerateSecp256k1 generates a new secp256k1 private key. | ||
func GenerateSecp256k1() (*secp256k1.PrivateKey, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
question: You only need this for testing right? not for any production code?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I'm planning on adding a way to generate and store them in the keyring as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some more questions.
Question: Was the ability to send requests without an identity (optionality of an identity being set or specified) removed? IMO we need to maintain optional identity.
Question: If Identity is still optional, when a verification fails, do you error out or assume that no identity was provided and still execute request? IMO should be an error even if optional identity is stripped.
No
If verification fails the request returns a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Re-requesting changes, you appear to have forgotten about/forgotten to push a few of my previous todos.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, thanks Keenan! I would prefer that you wait on Shahzad's approval before merge though :)
reviewing :) 👀 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Obviously there is on going work with this part of the codebase, approving now so I don't further block the merging. Only one todo, and some thoughts/questions
http/handler.go
Outdated
@@ -74,15 +76,26 @@ func NewHandler(db client.DB) (*Handler, error) { | |||
if err != nil { | |||
return nil, err | |||
} | |||
// generate a secure random audience value | |||
audienceBytes, err := crypto.RandomBytes(64) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
question: Could we use the peerID of the node as the audience, instead of random bytes? At the moment, restarting the node will invalidate previous JWS, since this creates a new audience value every time the node starts, where as the peer-id is initially random (function of nodes peer key) but constant after initialization.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem I ran into is that the peer ID can't easily be retrieved when p2p is disabled.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't that a good thing that the new audience value can be assigned to enforce re-validation using new JWS so previous ones can't be used (for whatever reason, some token got compromised or something).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Audience is now the api host name. Will update to include peer ID in a follow up PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks Keenen for doing the changes. I have some more comments that I have posted, please ping me once ready for re-review, and will give another final review :) Looks really nice now and I am super excited to have this in.
Sorry I forgot to post the action item points from our zoom call yesterday, I know you have done some of those items, but just leaving them here for reference to knock them all down pre-merge.
- Explicit empty identity in the tests that it was removed from
- Use identity option before checking private key, or re-think the option data struct now that identity must have private before verification and will only have public key after verfication.
- Documentation requests:
- Document how to generate hex from private key for CLI
- Document why we use HEX for CLI
- Document HEX type for decoding and encoding
- Document HTTP auth-flow
- Document command using
/audiance
endpoint/domain for HTTP - Document how to get the signed token with HTTP
- Document where to put the signed token with HTTP (i.e.
Bearer
often times people confuse this with GQL Variable haha)
acp/README.md
Outdated
read EC key | ||
Private-Key: (256 bit) | ||
priv: | ||
e3:b7:22:90:6e:e4:e5:63:68:f5:81:cd:8b:18:ab: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
todo: for now a one liner about removing colons or the command I shared below in the documentation would be nice IMO
acp/README.md
Outdated
defradb client acp policy add -f examples/dpi_policy/user_dpi_policy.yml \ | ||
--identity e3b722906ee4e56368f581cd8b18ab0f48af1ea53e635e3f7b8acd076676f6ac |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
todo: remove the \
added because now when you copy the command and dump to terminal it will assume there are separate commands, its a readme so avoid the \
and just have the long command so it's simple to copy and paste.
defradb client acp policy add -f examples/dpi_policy/user_dpi_policy.yml \ | |
--identity e3b722906ee4e56368f581cd8b18ab0f48af1ea53e635e3f7b8acd076676f6ac | |
defradb client acp policy add -f examples/dpi_policy/user_dpi_policy.yml --identity e3b722906ee4e56368f581cd8b18ab0f48af1ea53e635e3f7b8acd076676f6ac |
defradb client acp policy add -i cosmos1f2djr7dl9vhrk3twt3xwqp09nhtzec9mdkf70j -f examples/dpi_policy/user_dpi_policy.yml | ||
|
||
defradb client acp policy add -f examples/dpi_policy/user_dpi_policy.yml \ | ||
--identity e3b722906ee4e56368f581cd8b18ab0f48af1ea53e635e3f7b8acd076676f6ac | ||
``` | ||
|
||
Result: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nitpick: The Results are not true anymore, it will now be:
{
"PolicyID": "50d354a91ab1b8fce8a0ae4693de7616fb1d82cfc540f25cfbe11eb0195a5765"
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Therefore the example in examples/schema/permissioned/users.graphql
id
will need to update too.
@@ -28,7 +28,8 @@ Example: delete by docID: | |||
defradb client collection delete --name User --docID bae-123 | |||
|
|||
Example: delete by docID with identity: | |||
defradb client collection delete -i cosmos1f2djr7dl9vhrk3twt3xwqp09nhtzec9mdkf70j --name User --docID bae-123 | |||
defradb client collection delete --name User --docID bae-123 \ | |||
-i 028d53f37a19afb9a0dbc5b4be30c65731479ee8cfa0c9bc8f8bf198cc3c075f |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nitpick: doesn't the extra trailspace in next line after \
break the bash command if you copy paste into terminal?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It works for me
http/auth.go
Outdated
return jwt.NewBuilder(). | ||
Subject(subject). | ||
Audience([]string{audience}). | ||
Expiration(time.Now().Add(15 * time.Minute)). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
todo: the expire time should be made a const maybe a const on top of this file with authSchemaPrefix
, should be easier to make this tweakable in future.
todo: Might be worth documenting the default expiry time of the token is 15 mins somewhere.
tests/clients/cli/wrapper_cli.go
Outdated
@@ -58,8 +59,8 @@ func (w *cliWrapper) executeStream(ctx context.Context, args []string) (io.ReadC | |||
args = append(args, "--tx", fmt.Sprintf("%d", tx.ID())) | |||
} | |||
id := db.GetContextIdentity(ctx) | |||
if id.HasValue() { | |||
args = append(args, "--identity", id.Value().String()) | |||
if id.Value().PrivateKey != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: I would still like this id.HasValue()
check before the privateKey not nill check IMO
@@ -57,12 +66,18 @@ func TestACP_AddPolicy_InvalidCreatorIdentityWithValidPolicy_Error(t *testing.T) | |||
|
|||
func TestACP_AddPolicy_InvalidCreatorIdentityWithEmptyPolicy_Error(t *testing.T) { | |||
test := testUtils.TestCase{ | |||
// Using an invalid creator is not possible with other client | |||
// types as the address is derived from the public key used |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
question: is there a typo? public key used to signed the jwt or the private key?
tests/integration/acp/fixture.go
Outdated
) | ||
|
||
func init() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
question: Why does this need to be in an init
function?
todo: I would much prefer a util function some what like this:
func toIdentity(privateHex string) immutable.Option[acpIdentity.Identity] {
privateKeyBytes, err := hex.DecodeString(privateHex)
if err != nil {
panic(err)
}
return acpIdentity.FromPrivateKey(
secp256k1.PrivKeyFromBytes(
privateKeyBytes,
),
)
}
var (
Actor1Identity = toIdentity("028d53f37a19afb9a0dbc5b4be30c65731479ee8cfa0c9bc8f8bf198cc3c075f")
Actor2Identity = toIdentity("4d092126012ebaf56161716018a71630d99443d9d5217e9d8502bb5c5456f2c5")
)
suggestion: Something similar can replace the loadIdentity
function you have in the other test as well.
@@ -39,7 +39,7 @@ resources: | |||
- actor | |||
` | |||
|
|||
// policy id: "68a4e64d5034b8a0565a90cd36483de0d61e0ea2450cf57c1fa8d27cbbf17c2c" | |||
// policy id: "e3c35f345c844e8c0144d793933ea7287af1930d36e9d7d98e8d930fb9815a4a" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
praise: Thanks for maintaining the documentation accuracy here, would also be worth fixing the user facing examples in the example directory and the acp readme as well (I think I mentioned in other comments).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome work @nasdf appreciate you doing all the changes :)
One last nitpick, before merge if you can change the title of the PR to have the first word as an action verb instead of ACP
it would be nice.
For example, feat: Add Authentication for ACP
Relevant issue(s)
Resolves #2017
Description
This PR adds ACP identity authentication via HTTP.
Notable changes:
acp/identity
has been replaced with theacp.Identity
structidentity.PrivateKey
is the private key of the identityidentity.PublicKey
is the public key of the identityidentity.Address
is the bech32 formatted address for the identitysecp256k1
http
can authenticate requests using a jwt bearer tokena randomaudience
value is generated on every http server startupapi route/audience
returns the random audience valuehttp.Client
will create a signed token if anacp.PrivateKeyIdentity
is setcli
--identity
flag is now a hex encoded private keyTodo:
Tasks
How has this been tested?
make test
Specify the platform(s) on which this was tested: