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

Feature: Asymmetric Key Auth #3

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions .drone.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
type: docker
kind: pipeline
name: "Main"

steps:
- name: Docker build Git SHA
image: plugins/docker:20
pull: if-not-exists
environment:
DOCKER_BUILDKIT: 1
settings:
username:
from_secret: quay_username
password:
from_secret: quay_password
repo: quay.io/openware/gotrue
registry: quay.io
purge: false
when:
event:
- push
branch:
- stable/ow
- feature/asymmetric-auth
7 changes: 6 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ ENV GO111MODULE=on
ENV CGO_ENABLED=0
ENV GOOS=linux

RUN apk add --no-cache make git
RUN apk add --no-cache make git curl

WORKDIR /go/src/github.com/netlify/gotrue

Expand All @@ -15,11 +15,16 @@ RUN make deps
COPY . /go/src/github.com/netlify/gotrue
RUN make build

ARG KAIGARA_VERSION=0.1.34
RUN curl -Lo ./kaigara https://github.com/openware/kaigara/releases/download/${KAIGARA_VERSION}/kaigara \
&& chmod +x ./kaigara

FROM alpine:3.15
RUN adduser -D -u 1000 netlify

RUN apk add --no-cache ca-certificates
COPY --from=build /go/src/github.com/netlify/gotrue/gotrue /usr/local/bin/gotrue
COPY --from=build /go/src/github.com/netlify/gotrue/kaigara /usr/local/bin/kaigara
COPY --from=build /go/src/github.com/netlify/gotrue/migrations /usr/local/etc/gotrue/migrations/

ENV GOTRUE_DB_MIGRATIONS_PATH /usr/local/etc/gotrue/migrations
Expand Down
55 changes: 54 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,13 +167,17 @@ The name to use for the service.

```properties
GOTRUE_JWT_SECRET=supersecretvalue
GOTRUE_JWT_ALGORITHM=RS256
GOTRUE_JWT_EXP=3600
GOTRUE_JWT_AUD=netlify
```
`JWT_ALGORITHM` - `string`

The signing algorithm for the JWT. Defaults to HS256.

`JWT_SECRET` - `string` **required**

The secret used to sign JWT tokens with.
The secret used to sign JWT tokens with. If signing alogrithm is RS256, secret has to be Base64 encoded RSA private key.

`JWT_EXP` - `number`

Expand Down Expand Up @@ -936,3 +940,52 @@ External provider should redirect to here

Redirects to `<GOTRUE_SITE_URL>#access_token=<access_token>&refresh_token=<refresh_token>&provider_token=<provider_oauth_token>&expires_in=3600&provider=<provider_name>`
If additional scopes were requested then `provider_token` will be populated, you can use this to fetch additional data from the provider or interact with their services
### **POST /sign_challenge**

This is an endpoint for user sign up with Asymmetric key.
Currently implemets only sign up with Ethereum address( not public key).

body:
```json
// Sign up with Metamask browser extension
{
"key": "0x6BE46d7D863666546b77951D5dfffcF075F36E68",
"algorithm": "ETH"
}
```

Returns:
```json
{
"challenge_token": "d188f5a4-f9d6-4ede-8cfd-2a45927b0edc"
}
```
Returned challenge token has to be signed with Metamask and sent back to /asymmetric_login

### **POST /asymmetric_login**

This is an endpoint for user sign in with Asymmetric key.
Accepts signed challenge token from `/sign_challenge` endpoint

body:
```json
// Login with with Metamask browser extension
{
"key": "0x6BE46d7D863666546b77951D5dfffcF075F36E68",
"challenge_token_signature": "0x3129682f92a0f3f6ef648623c3256ae39ab16de4fefcc50c60a375c8dd224dde291f750d0fd3d475b403a00a631dd8979583b8d036d2e3b2408668a1b4ea6b321c"
}
```

Returns:
```json
{
"access_token": "jwt-token-representing-the-user",
"token_type": "bearer",
"expires_in": 3600,
"refresh_token": "a-refresh-token"
}
```

Once you have an access token, you can access the methods requiring authentication
by settings the `Authorization: Bearer YOUR_ACCESS_TOKEN_HERE` header.

15 changes: 11 additions & 4 deletions api/admin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,16 @@ func (ts *AdminTestSuite) makeSuperAdmin(email string) string {

u.Role = "supabase_admin"

token, err := generateAccessToken(u, time.Second*time.Duration(ts.Config.JWT.Exp), ts.Config.JWT.Secret)

key, err := models.FindMainAsymmetricKeyByUser(ts.API.db, u)
require.NoError(ts.T(), err, "Error finding keys")

token, err := generateAccessToken(u, key, time.Second*time.Duration(ts.Config.JWT.Exp), ts.Config.JWT.GetSigningMethod(), ts.Config.JWT.GetSigningKey())
require.NoError(ts.T(), err, "Error generating access token")

p := jwt.Parser{ValidMethods: []string{jwt.SigningMethodHS256.Name}}
_, err = p.Parse(token, func(token *jwt.Token) (interface{}, error) {
return []byte(ts.Config.JWT.Secret), nil
return ts.Config.JWT.GetVerificationKey(), nil
})
require.NoError(ts.T(), err, "Error parsing token")

Expand All @@ -70,12 +74,15 @@ func (ts *AdminTestSuite) makeSystemUser() string {
u := models.NewSystemUser(uuid.Nil, ts.Config.JWT.Aud)
u.Role = "service_role"

token, err := generateAccessToken(u, time.Second*time.Duration(ts.Config.JWT.Exp), ts.Config.JWT.Secret)
key, err := models.FindMainAsymmetricKeyByUser(ts.API.db, u)
require.NoError(ts.T(), err, "Error finding keys")

token, err := generateAccessToken(u, key, time.Second*time.Duration(ts.Config.JWT.Exp), ts.Config.JWT.GetSigningMethod(), ts.Config.JWT.GetSigningKey())
require.NoError(ts.T(), err, "Error generating access token")

p := jwt.Parser{ValidMethods: []string{jwt.SigningMethodHS256.Name}}
_, err = p.Parse(token, func(token *jwt.Token) (interface{}, error) {
return []byte(ts.Config.JWT.Secret), nil
return ts.Config.JWT.GetVerificationKey(), nil
})
require.NoError(ts.T(), err, "Error parsing token")

Expand Down
2 changes: 2 additions & 0 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ func NewAPIWithVersion(ctx context.Context, globalConfig *conf.GlobalConfigurati
sharedLimiter := api.limitEmailSentHandler()
r.With(sharedLimiter).With(api.requireAdminCredentials).Post("/invite", api.Invite)
r.With(sharedLimiter).With(api.verifyCaptcha).Post("/signup", api.Signup)
r.With(sharedLimiter).With(api.verifyCaptcha).Post("/sign_challenge", api.GetChallengeToken)
r.With(sharedLimiter).With(api.verifyCaptcha).Post("/asymmetric_login", api.SignInWithAsymmetricKey)
r.With(sharedLimiter).With(api.verifyCaptcha).With(api.requireEmailProvider).Post("/recover", api.Recover)
r.With(sharedLimiter).With(api.verifyCaptcha).Post("/magiclink", api.MagicLink)

Expand Down
Loading