[server][cli] Generate API Keys/Tokens from CLI

This commit is contained in:
Abhishek Shroff
2025-07-14 16:15:57 +05:30
parent 2d0fb18265
commit b1a6c7dcdc
5 changed files with 100 additions and 6 deletions
+4 -2
View File
@@ -12,8 +12,10 @@ import (
"github.com/jackc/pgx/v5/pgtype"
)
var apiTokenEncoder = base64.StdEncoding.WithPadding(base64.NoPadding)
func ReadEncodedAPIKey(db db.Handler, encodedKey string) (Auth, error) {
if b, err := base64.StdEncoding.DecodeString(encodedKey); err != nil {
if b, err := apiTokenEncoder.DecodeString(encodedKey); err != nil {
return nil, ErrCredentialsInvalid
} else if len(b) < 16 {
return nil, ErrCredentialsInvalid
@@ -69,6 +71,6 @@ func GenerateEncodedAPIKey(db db.TxHandler, userID int32, validity time.Duration
if id, key, err := GenerateAPIKey(db, userID, validity, description, scopes); err != nil {
return "", err
} else {
return base64.StdEncoding.EncodeToString(append(id[:], key...)), nil
return apiTokenEncoder.EncodeToString(append(id[:], key...)), nil
}
}
+2 -2
View File
@@ -56,7 +56,7 @@ VALUES (@token_id, @expires, @token_hash, @oidc_provider, @oidc_client_type, @oi
q.Add("client_id", clientID)
q.Add("response_type", "code")
q.Add("scope", "openid email profile")
q.Add("state", tokenEncoder.EncodeToString(append(tokenID[:], token...)))
q.Add("state", TokenEncoder.EncodeToString(append(tokenID[:], token...)))
q.Add("redirect_uri", redirectURI)
q.Add("code_challenge", codeChallenge)
q.Add("code_challenge_method", "S256")
@@ -70,7 +70,7 @@ func OpenIDValidateAuthCode(d db.Handler, state, authCode, redirectURI string) (
var tokenID uuid.UUID
var tokenHash []byte
if b, err := tokenEncoder.DecodeString(state); err != nil {
if b, err := TokenEncoder.DecodeString(state); err != nil {
return OpenIDClientNone, ErrTokenInvalid
} else if len(b) < 16 {
return OpenIDClientNone, ErrTokenInvalid
+2 -2
View File
@@ -12,10 +12,10 @@ import (
"github.com/jackc/pgx/v5"
)
var tokenEncoder = base32.StdEncoding.WithPadding(base32.NoPadding)
var TokenEncoder = base32.StdEncoding.WithPadding(base32.NoPadding)
func PerformTokenLogin(db db.TxHandler, encodedToken string) (Auth, string, error) {
if b, err := tokenEncoder.DecodeString(encodedToken); err != nil {
if b, err := TokenEncoder.DecodeString(encodedToken); err != nil {
return nil, "", ErrCredentialsInvalid
} else if len(b) < 16 {
return nil, "", ErrCredentialsInvalid
+2
View File
@@ -2,6 +2,7 @@ package user
import (
"codeberg.org/shroff/phylum/server/internal/command/user/bookmarks"
"codeberg.org/shroff/phylum/server/internal/command/user/keys"
"github.com/spf13/cobra"
)
@@ -12,6 +13,7 @@ func SetupCommand() *cobra.Command {
}
cmd.PersistentFlags().StringP("user-email", "u", "", "User")
cmd.AddCommand([]*cobra.Command{
keys.SetupCommand(),
bookmarks.SetupCommand(),
}...)
+90
View File
@@ -0,0 +1,90 @@
package keys
import (
"bufio"
"context"
"fmt"
"os"
"strings"
"time"
"codeberg.org/shroff/phylum/server/internal/auth"
"codeberg.org/shroff/phylum/server/internal/command/common"
"codeberg.org/shroff/phylum/server/internal/db"
"github.com/spf13/cobra"
)
func SetupCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "keys",
Short: "Manager API Keys",
}
cmd.AddCommand(
setupGenerateCommand(),
)
return cmd
}
func setupGenerateCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "generate",
Short: "Generate New API Key/Token",
Run: func(cmd *cobra.Command, args []string) {
u := common.User(cmd)
validity, _ := cmd.Flags().GetDuration("validity")
description, _ := cmd.Flags().GetString("description")
if description == "" {
description = "Generated via CLI on " + time.Now().Format(time.DateTime)
}
var scopes []string
if len(scopes) == 0 {
reader := bufio.NewReader(os.Stdin)
name, _ := cmd.Flags().GetString("name")
if name == "" {
fmt.Print("Comma-separated scopes [*]: ")
if val, err := reader.ReadString('\n'); err != nil {
fmt.Println("failed to read scopes: " + err.Error())
os.Exit(1)
} else if len(strings.TrimSpace(val)) == 0 {
scopes = []string{"*"}
} else {
scopes = strings.Split(val, ",")
for i, s := range scopes {
scopes[i] = strings.TrimSpace(s)
}
}
}
}
if err := db.Get(context.Background()).RunInTx(func(db db.TxHandler) error {
if token, _ := cmd.Flags().GetBool("token"); token {
if token, err := auth.GenerateEncodedAPIKey(db, u.ID, validity, description, scopes); err != nil {
return err
} else {
fmt.Println(token)
return nil
}
} else {
if id, key, err := auth.GenerateAPIKey(db, u.ID, validity, description, scopes); err != nil {
return err
} else {
fmt.Println("Key ID:", id.String())
fmt.Println(" Key:", auth.TokenEncoder.EncodeToString(key))
return nil
}
}
}); err != nil {
fmt.Println("unable to generate API key: " + err.Error())
os.Exit(1)
}
},
}
cmd.Flags().BoolP("token", "t", false, "Generate API token instead of ID:Key pair")
cmd.Flags().Duration("validity", time.Duration(0), "Validity of API Key (0 means never)")
cmd.Flags().String("description", "", "Validity of API Key (0 means never)")
cmd.Flags().StringSlice("scopes", []string{}, "Scopes")
return cmd
}