@@ -8,12 +8,17 @@ import (
88
99 "github.com/spf13/cobra"
1010 "github.com/tursodatabase/turso-cli/internal"
11+ "github.com/tursodatabase/turso-cli/internal/flags"
1112 "github.com/tursodatabase/turso-cli/internal/prompt"
1213 "github.com/tursodatabase/turso-cli/internal/settings"
1314 "github.com/tursodatabase/turso-cli/internal/turso"
1415)
1516
1617var adminFlag bool
18+ var jwksRegion string
19+ var jwksDatabase string
20+ var jwksGroup string
21+ var jwksScope string
1722
1823func init () {
1924 rootCmd .AddCommand (orgCmd )
@@ -38,6 +43,21 @@ func init() {
3843 orgCmd .AddCommand (orgBillingCmd )
3944 membersAddCmd .Flags ().BoolVarP (& adminFlag , "admin" , "a" , false , "Add the user as an admin" )
4045 membersInviteCmd .Flags ().BoolVarP (& adminFlag , "admin" , "a" , false , "Invite the user as an admin" )
46+
47+ orgCmd .AddCommand (jwksCmd )
48+ jwksCmd .AddCommand (jwksList )
49+ jwksCmd .AddCommand (jwksRemove )
50+ jwksCmd .AddCommand (jwksSave )
51+ jwksCmd .AddCommand (jwksTemplate )
52+
53+ jwksSave .Flags ().StringVarP (& jwksRegion , "region" , "r" , "" , "region" )
54+ jwksSave .Flags ().MarkHidden ("region" )
55+ jwksRemove .Flags ().StringVarP (& jwksRegion , "region" , "r" , "" , "region" )
56+ jwksRemove .Flags ().MarkHidden ("region" )
57+ jwksTemplate .Flags ().StringVarP (& jwksDatabase , "database" , "d" , "" , "database" )
58+ jwksTemplate .Flags ().StringVarP (& jwksGroup , "group" , "g" , "" , "group" )
59+ jwksTemplate .Flags ().StringVarP (& jwksScope , "scope" , "s" , "full-access" , "claims scope (full-access or read-only)" )
60+ flags .AddFineGrainedPermissions (jwksTemplate )
4161}
4262
4363func switchToOrg (client * turso.Client , slug string , showHowToGoBack bool ) error {
@@ -622,6 +642,165 @@ func BillingPortal() error {
622642 return billingPortal (org )
623643}
624644
645+ func currentOrg (client * turso.Client ) (turso.Organization , error ) {
646+ orgs , err := client .Organizations .List ()
647+ if err != nil {
648+ return turso.Organization {}, err
649+ }
650+
651+ settingsObj , err := settings .ReadSettings ()
652+ if err != nil {
653+ return turso.Organization {}, err
654+ }
655+
656+ current := settingsObj .Organization ()
657+
658+ for _ , org := range orgs {
659+ if isCurrentOrg (org , current ) {
660+ return org , nil
661+ }
662+ }
663+ return turso.Organization {}, fmt .Errorf ("current organization is not set" )
664+ }
665+
666+ var jwksCmd = & cobra.Command {
667+ Use : "jwks" ,
668+ Short : "[BETA] Manage your organization external JWKS sources" ,
669+ }
670+
671+ var jwksList = & cobra.Command {
672+ Use : "list" ,
673+ Short : "List saved external JWKS sources" ,
674+ ValidArgsFunction : noFilesArg ,
675+ RunE : func (cmd * cobra.Command , args []string ) error {
676+ cmd .SilenceUsage = true
677+
678+ client , err := authedTursoClient ()
679+ if err != nil {
680+ return err
681+ }
682+
683+ org , err := currentOrg (client )
684+ if err != nil {
685+ return err
686+ }
687+
688+ jwksList , err := client .Organizations .ListJwks (org .Slug )
689+ if err != nil {
690+ return err
691+ }
692+ table := make ([][]string , 0 )
693+ for _ , jwks := range jwksList {
694+ table = append (table , []string {jwks .JwksName , jwks .JwksUrl })
695+ }
696+ printTable ([]string {"name" , "url" }, table )
697+ return nil
698+ },
699+ }
700+
701+ var jwksTemplate = & cobra.Command {
702+ Use : "template" ,
703+ Short : "Generate JWT claims template for external auth provider" ,
704+ ValidArgsFunction : noFilesArg ,
705+ RunE : func (cmd * cobra.Command , args []string ) error {
706+ cmd .SilenceUsage = true
707+
708+ client , err := authedTursoClient ()
709+ if err != nil {
710+ return err
711+ }
712+
713+ org , err := currentOrg (client )
714+ if err != nil {
715+ return err
716+ }
717+
718+ var database * string
719+ var group * string
720+
721+ if jwksDatabase != "" {
722+ database = & jwksDatabase
723+ }
724+ if jwksGroup != "" {
725+ group = & jwksGroup
726+ }
727+ permissions , err := flags .FineGrainedPermissionsFlags ()
728+ if err != nil {
729+ return err
730+ }
731+ params := turso.OrgJwksTemplateParams {
732+ Database : database ,
733+ Group : group ,
734+ Scope : jwksScope ,
735+ Permissions : permissions ,
736+ }
737+ template , err := client .Organizations .JwksTemplate (org .Slug , params )
738+ if err != nil {
739+ return err
740+ }
741+ fmt .Printf ("%v\n " , internal .Emph (template ))
742+ return nil
743+ },
744+ }
745+
746+ var jwksSave = & cobra.Command {
747+ Use : "save <name> <url>" ,
748+ Short : "Save external JWKS source with given name and URL (e.g. save clerk https://.../.well-known/jwks.json)" ,
749+ Args : cobra .ExactArgs (2 ),
750+ ValidArgsFunction : noFilesArg ,
751+ RunE : func (cmd * cobra.Command , args []string ) error {
752+ cmd .SilenceUsage = true
753+
754+ name , url := args [0 ], args [1 ]
755+
756+ client , err := authedTursoClient ()
757+ if err != nil {
758+ return err
759+ }
760+
761+ org , err := currentOrg (client )
762+ if err != nil {
763+ return err
764+ }
765+
766+ err = client .Organizations .SaveJwks (org .Slug , name , url , jwksRegion )
767+ if err != nil {
768+ return err
769+ }
770+ fmt .Printf ("JWKS %v saved for organization %s.\n " , internal .Emph (name ), internal .Emph (org .Slug ))
771+ return nil
772+ },
773+ }
774+
775+ var jwksRemove = & cobra.Command {
776+ Use : "remove <name>" ,
777+ Short : "Remove external JWKS source with given name" ,
778+ Args : cobra .ExactArgs (1 ),
779+ ValidArgsFunction : noFilesArg ,
780+ RunE : func (cmd * cobra.Command , args []string ) error {
781+ cmd .SilenceUsage = true
782+
783+ name := args [0 ]
784+
785+ client , err := authedTursoClient ()
786+ if err != nil {
787+ return err
788+ }
789+
790+ org , err := currentOrg (client )
791+ if err != nil {
792+ return err
793+ }
794+
795+ err = client .Organizations .RemoveJwks (org .Slug , name , jwksRegion )
796+ if err != nil {
797+ return err
798+ }
799+ fmt .Printf ("JWKS %v removed from organization %s.\n " , internal .Emph (name ), internal .Emph (org .Slug ))
800+ return nil
801+ },
802+ }
803+
625804func listOrganizations (client * turso.Client , fresh ... bool ) ([]turso.Organization , error ) {
626805 skipCache := len (fresh ) > 0 && fresh [0 ]
627806 if cache := getOrgsCache (); ! skipCache && cache != nil {
0 commit comments