Skip to content

Commit

Permalink
feat: Auth0 migration (#73)
Browse files Browse the repository at this point in the history
* feat: Read users from auth0 export

* feat: Read passwords form json file

* feat: migration tool auth0

* feat: remove unused code

* feat: remove unused file

* feat: add migration tool and readme

* feat: readme

* feat: readme

* feat: clean up migration file

* fix: remove unused file

* refactor: move commands into single binary

* merge main and update dependencies

---------

Co-authored-by: Livio Spring <[email protected]>
  • Loading branch information
hifabienne and livio-a authored Apr 21, 2023
1 parent 419bfa0 commit 3a07a3c
Show file tree
Hide file tree
Showing 14 changed files with 435 additions and 146 deletions.
1 change: 0 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,5 @@ jobs:
with:
version: latest
args: release --rm-dist
workdir: ./cmd/jwt/
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
2 changes: 1 addition & 1 deletion cmd/jwt/.goreleaser.yml → .goreleaser.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
project_name: key2jwt
project_name: zitadel-tools

builds:
- env:
Expand Down
12 changes: 8 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,18 @@ key2jwt requires two flags:
The tool prints the result to standard output.

```zsh
./key2jwt -audience=https://zitadel.cloud -key=key.json
./zitadel-tools key2jwt -audience=https://zitadel.cloud -key=key.json
```

Optionally you can pass an `output` flag. This will save the jwt in the provided file path:

```zsh
./key2jwt -audience=https://zitadel.cloud -key=key.json -output=jwt.txt
./zitadel-tools key2jwt -audience=https://zitadel.cloud -key=key.json -output=jwt.txt
```

You can also create a JWT by providing a RSA private key (.pem file). You then also need to specify the issuer of the token:
```zsh
./key2jwt -audience=https://zitadel.cloud -key=key.pem -issuer=client_id
./zitadel-tools key2jwt -audience=https://zitadel.cloud -key=key.pem -issuer=client_id
```

## basicauth
Expand All @@ -42,5 +42,9 @@ basicauth requires two flags:
The tool prints the URL- and Base64 encoded result to standard output

```zsh
go run ./cmd/basicauth/*.go -id $CLIENT_ID -secret $CLIENT_SECRET
./zitadel-tools basicauth -id $CLIENT_ID -secret $CLIENT_SECRET
```

## Migrate data (e.g. Auth0) to ZITADEL import

Please check the description in the [migration section](./cmd/migration/auth0).
37 changes: 26 additions & 11 deletions cmd/basicauth/basicauth.go
Original file line number Diff line number Diff line change
@@ -1,28 +1,43 @@
package main
package basicauth

import (
b64 "encoding/base64"
"flag"
"fmt"
"log"
"net/url"

"github.com/spf13/cobra"
)

// Cmd represents the basicauth command
var Cmd = &cobra.Command{
Use: "basicauth",
Short: "Convert <client ID> and <client secret> to be used in Authorization header for Client Secret Basic",
Run: func(cmd *cobra.Command, args []string) {
basicAuth(cmd)
},
}

var (
clientId = flag.String("id", "", "Client ID as string")
clientSecret = flag.String("secret", "", "Client secret as string")
clientId string
clientSecret string
)

func main() {
flag.Parse()
func init() {
Cmd.Flags().StringVar(&clientId, "id", "", "Client ID as string")
Cmd.Flags().StringVar(&clientSecret, "secret", "", "Client secret as string")
}

if *clientId == "" || *clientSecret == "" {
flag.PrintDefaults()
panic("please provide a client ID and secret")
func basicAuth(cmd *cobra.Command) {
if clientId == "" || clientSecret == "" {
log.Println("please provide a client ID and secret")
fmt.Println(cmd.Flags().FlagUsages())
return
}

sEscaped := url.QueryEscape(*clientId) + ":" + url.QueryEscape(*clientSecret)
sEscaped := url.QueryEscape(clientId) + ":" + url.QueryEscape(clientSecret)

sEnc := b64.StdEncoding.EncodeToString([]byte(sEscaped))

fmt.Print(sEnc)
fmt.Println(sEnc)
}
71 changes: 42 additions & 29 deletions cmd/jwt/key2jwt.go
Original file line number Diff line number Diff line change
@@ -1,62 +1,75 @@
package main
package jwt

import (
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"log"
"os"
"path/filepath"
"time"

"github.com/spf13/cobra"
"github.com/zitadel/oidc/pkg/client"
"github.com/zitadel/oidc/pkg/oidc"
)

// Cmd represents the jwt command
var Cmd = &cobra.Command{
Use: "key2jwt",
Short: "Convert a <key file> to <jwt token>",
Run: func(cmd *cobra.Command, args []string) {
key2JWT(cmd)
},
}

var (
keyPath = flag.String("key", "", "path to the key.json / RSA private key.pem")
audience = flag.String("audience", "", "audience where the token will be used (e.g. the issuer of zitadel.cloud - https://zitadel.cloud or from your domain https://<your domain>)")
issuer = flag.String("issuer", "", "issuer of the JWT (e.g. userID / client_id; only needed when generating from RSA private key)")
outputPath = flag.String("output", "", "path where the generated jwt will be saved; will print to stdout if empty")
keyPath string
audience string
issuer string
outputPath string
)

func main() {
flag.Parse()
func init() {
Cmd.Flags().StringVar(&keyPath, "key", "", "path to the key.json / RSA private key.pem")
Cmd.Flags().StringVar(&audience, "audience", "", "audience where the token will be used (e.g. the issuer of zitadel.cloud - https://zitadel.cloud or from your domain https://<your domain>)")
Cmd.Flags().StringVar(&issuer, "issuer", "", "issuer of the JWT (e.g. userID / client_id; only needed when generating from RSA private key)")
Cmd.Flags().StringVar(&outputPath, "output", "", "path where the generated jwt will be saved; will print to stdout if empty")
}

if *keyPath == "" || *audience == "" {
fmt.Println("Please provide at least an audience and key param:")
flag.PrintDefaults()
func key2JWT(cmd *cobra.Command) {
if keyPath == "" || audience == "" {
log.Println("Please provide at least an audience and key param:")
fmt.Println(cmd.LocalFlags().FlagUsages())
return
}

key, err := ioutil.ReadFile(*keyPath)
key, err := os.ReadFile(keyPath)
if err != nil {
fmt.Printf("error reading key file: %v", err.Error())
log.Fatalf("error reading key file: %v", err.Error())
return
}
var jwt string
switch ext := filepath.Ext(*keyPath); ext {
switch ext := filepath.Ext(keyPath); ext {
case ".json":
jwt, err = generateJWTFromJSON(key)
case ".pem":
if *issuer == "" {
fmt.Println("Please provide the issuer of token when using a pem file")
return
if issuer == "" {
log.Fatal("Please provide the issuer of token when using a pem file")
}
jwt, err = generateJWTFromPEM(key, *issuer)
jwt, err = generateJWTFromPEM(key, issuer)
default:
fmt.Printf("file extension %v is not supported, please provide either a json or pem file\n", ext)
log.Fatalf("file extension %v is not supported, please provide either a json or pem file\n", ext)
return
}
if err != nil {
fmt.Printf("error generating jwt: %v", err.Error())
log.Fatalf("error generating jwt: %v", err.Error())
return
}
f := os.Stdout
if *outputPath != "" {
f, err = os.OpenFile(*outputPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0777)
if outputPath != "" {
f, err = os.OpenFile(outputPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0777)
if err != nil {
fmt.Printf("error reading key file: %v", err.Error())
log.Fatalf("error reading key file: %v", err.Error())
return
}
}
Expand All @@ -65,7 +78,7 @@ func main() {
err = errClose
}
if err != nil {
fmt.Printf("error writing key: %v", err.Error())
log.Fatalf("error writing key: %v", err.Error())
return
}
}
Expand All @@ -77,17 +90,17 @@ func generateJWTFromJSON(key []byte) (string, error) {
}
switch keyType {
case "application":
keyData, err := client.ConfigFromKeyFile(*keyPath)
keyData, err := client.ConfigFromKeyFile(keyPath)
if err != nil {
return "", err
}
signer, err := client.NewSignerFromPrivateKeyByte([]byte(keyData.Key), keyData.KeyID)
if err != nil {
return "", err
}
return client.SignedJWTProfileAssertion(keyData.ClientID, []string{*audience}, time.Hour, signer)
return client.SignedJWTProfileAssertion(keyData.ClientID, []string{audience}, time.Hour, signer)
case "serviceaccount":
jwta, err := oidc.NewJWTProfileAssertionFromFileData(key, []string{*audience})
jwta, err := oidc.NewJWTProfileAssertionFromFileData(key, []string{audience})
if err != nil {
return "", err
}
Expand All @@ -102,7 +115,7 @@ func generateJWTFromPEM(key []byte, issuer string) (string, error) {
if err != nil {
return "", err
}
return client.SignedJWTProfileAssertion(issuer, []string{*audience}, time.Hour, signer)
return client.SignedJWTProfileAssertion(issuer, []string{audience}, time.Hour, signer)
}

func getType(data []byte) (string, error) {
Expand Down
2 changes: 2 additions & 0 deletions cmd/migration/auth0/example-data/passwords.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{"_ID":{"$oid":"60425dc43519d90068f82973"},"email_verified":false,"email":"[email protected]","passwordHash":"$2b$10$Z6hUTEEeoJXN5/AmSm/4.eZ75RYgFVriQM9LPhNEC7kbAbS/VAaJ2","password_set_date":{"$date":"2021-03-05T16:35:16.775Z"},"tenant":"dev-rwsbs6ym","connection":"Username-Password-Authentication","_tmp_is_unique":true}
{"_ID":{"$oid":"60425da93519d90068f82966"},"email_verified":false,"email":"[email protected]","passwordHash":"$2b$10$CSZ2JarG4XYbGa.JkfpqnO2wrlbfp5eb5LScHSGo9XGeZ.a.Ic54S","password_set_date":{"$date":"2021-03-05T16:34:49.502Z"},"tenant":"dev-rwsbs6ym","connection":"Username-Password-Authentication","_tmp_is_unique":true}
2 changes: 2 additions & 0 deletions cmd/migration/auth0/example-data/users.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{"user_id":"auth0|6437e9c2b7add15a89b4915b","email":"[email protected]","name":"Test Example2"}
{"user_id":"auth0|6437fa205c7266dc77f88a6c","email":"[email protected]","name":"Test User"}
Loading

0 comments on commit 3a07a3c

Please sign in to comment.