Skip to content

Commit 5f2ec83

Browse files
committed
feat: update callback paths, default ports, and add API identifier collision handling
- Standardize callback paths for Next.js, Fastify, and Hono to "/auth/callback" - Update Spring Boot callback path to "/login/oauth2/code/okta" and set default port to 3000 - Set default ports for aspnet-mvc and aspnet-owin to 5000 - Add logic to detect and handle API identifier collisions by generating a unique suffix
1 parent 85691c5 commit 5f2ec83

5 files changed

Lines changed: 58 additions & 49 deletions

File tree

internal/auth0/quickstart.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,7 @@ var QuickstartConfigs = map[string]AppConfig{
405405
Callbacks: []string{DetectionSub},
406406
AllowedLogoutURLs: []string{DetectionSub},
407407
Name: DetectionSub,
408-
CallbackPath: "/api/auth/callback",
408+
CallbackPath: "/auth/callback",
409409
},
410410
Strategy: FileOutputStrategy{Path: ".env", Format: "dotenv"},
411411
AudienceVar: "AUTH0_AUDIENCE",
@@ -507,6 +507,7 @@ var QuickstartConfigs = map[string]AppConfig{
507507
Callbacks: []string{DetectionSub},
508508
AllowedLogoutURLs: []string{DetectionSub},
509509
Name: DetectionSub,
510+
CallbackPath: "/auth/callback",
510511
},
511512
Strategy: FileOutputStrategy{Path: ".env", Format: "dotenv"},
512513
},
@@ -597,8 +598,7 @@ var QuickstartConfigs = map[string]AppConfig{
597598
Callbacks: []string{DetectionSub},
598599
AllowedLogoutURLs: []string{DetectionSub},
599600
Name: DetectionSub,
600-
// okta-spring-boot-starter registers redirect under "oidc" registration ID.
601-
CallbackPath: "/login/oauth2/code/oidc",
601+
CallbackPath: "/login/oauth2/code/okta",
602602
},
603603
Strategy: FileOutputStrategy{Path: "src/main/resources/application.yml", Format: "yaml"},
604604
},
@@ -614,7 +614,7 @@ var QuickstartConfigs = map[string]AppConfig{
614614
Callbacks: []string{DetectionSub},
615615
AllowedLogoutURLs: []string{DetectionSub},
616616
Name: DetectionSub,
617-
CallbackPath: "/login/oauth2/code/oidc",
617+
CallbackPath: "/login/oauth2/code/okta",
618618
},
619619
Strategy: FileOutputStrategy{Path: "src/main/resources/application.yml", Format: "yaml"},
620620
},

internal/cli/quickstart_detect.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ func DetectProject(dir string) DetectionResult {
3535
// Read package.json deps early - needed for checks that must precede file-based signals.
3636
earlyDeps := readPackageJSONDeps(dir)
3737

38-
// -- 1. manage.py (Django) — checked before Ionic to prevent monorepo misdetection.
38+
// -- 1. Manage.py (Django) — checked before Ionic to prevent monorepo misdetection.
3939
if fileExists(dir, "manage.py") {
4040
result.Framework = "django"
4141
result.Type = "regular"
@@ -76,7 +76,7 @@ func DetectProject(dir string) DetectionResult {
7676
return result
7777
}
7878

79-
// -- 4. pubspec.yaml (Flutter) --.
79+
// -- 4. Pubspec.yaml (Flutter) --.
8080
if data, ok := readFileContent(dir, "pubspec.yaml"); ok {
8181
if strings.Contains(data, "sdk: flutter") {
8282
result.Detected = true

internal/cli/quickstart_detect_test.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1216,7 +1216,7 @@ func TestDefaultPortForFramework(t *testing.T) {
12161216
// Regular - PHP.
12171217
{"laravel", 8000},
12181218
// Regular - Java.
1219-
{"spring-boot", 8080},
1219+
{"spring-boot", 3000},
12201220
{"java-ee", 8080},
12211221
{"vanilla-java", 8080},
12221222
// Regular - default 3000.
@@ -1230,6 +1230,8 @@ func TestDefaultPortForFramework(t *testing.T) {
12301230
{"vanilla-go", 3000},
12311231
{"django", 8000},
12321232
// Native - default 3000.
1233+
{"aspnet-mvc", 5000},
1234+
{"aspnet-owin", 5000},
12331235
{"flutter", 3000},
12341236
{"react-native", 3000},
12351237
{"expo", 3000},

internal/cli/quickstarts.go

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ package cli
22

33
import (
44
"context"
5+
"crypto/rand"
56
_ "embed"
7+
"encoding/hex"
68
"encoding/json"
79
"fmt"
810
"net/url"
@@ -14,6 +16,7 @@ import (
1416
"sort"
1517
"strconv"
1618
"strings"
19+
"time"
1720

1821
"github.com/auth0/go-auth0/management"
1922
"github.com/spf13/cobra"
@@ -423,28 +426,6 @@ func (i *qsInputs) fromArgs(cmd *cobra.Command, args []string, cli *cli) error {
423426
return nil
424427
}
425428

426-
var (
427-
qsType = Flag{
428-
Name: "Type",
429-
LongForm: "type",
430-
ShortForm: "t",
431-
Help: "Type of quickstart (vite, nextjs, fastify, jhipster-rwa)",
432-
IsRequired: true,
433-
}
434-
qsAppName = Flag{
435-
Name: "Name",
436-
LongForm: "name",
437-
ShortForm: "n",
438-
Help: "Name of the Auth0 application (default: 'My App' for vite, nextjs and fastify, 'JHipster' for jhipster-rwa)",
439-
}
440-
qsPort = Flag{
441-
Name: "Port",
442-
LongForm: "port",
443-
ShortForm: "p",
444-
Help: "Port number for the application (default: 5173 for vite, 3000 for nextjs/fastify, 8080 for jhipster-rwa)",
445-
}
446-
)
447-
448429
// Flags for the setup command.
449430
var (
450431
setupApp = Flag{
@@ -849,6 +830,14 @@ func collectAPIInputs(cmd *cobra.Command, cli *cli, inputs *SetupInputs) error {
849830
if defaultID == "" && inputs.Name != "" {
850831
slug := strings.ToLower(strings.ReplaceAll(inputs.Name, " ", "-"))
851832
defaultID = "https://" + slug
833+
834+
// Check once if the clean slug is taken; if so, append a random suffix
835+
// so the rerun doesn't collide. Respects user input — only suggests, doesn't force.
836+
if _, err := cli.api.ResourceServer.Read(cmd.Context(), url.PathEscape(defaultID)); err == nil {
837+
suffixed := fmt.Sprintf("%s-%s", defaultID, randomSlugSuffix(4))
838+
cli.renderer.Warnf("API identifier %q is already in use. Generated a new identifier %q to avoid conflict.", defaultID, suffixed)
839+
defaultID = suffixed
840+
}
852841
}
853842
inputs.Identifier = defaultID
854843
if err := setupIdentifier.Ask(cmd, &inputs.Identifier, &defaultID); err != nil {
@@ -862,6 +851,11 @@ func collectAPIInputs(cmd *cobra.Command, cli *cli, inputs *SetupInputs) error {
862851
return err
863852
}
864853

854+
// Fail fast if the (possibly user-overridden) identifier is already taken — avoids creating an orphaned app.
855+
if _, err := cli.api.ResourceServer.Read(cmd.Context(), url.PathEscape(inputs.Identifier)); err == nil {
856+
return fmt.Errorf("an API with identifier %q already exists; use a different identifier or delete the existing API first", inputs.Identifier)
857+
}
858+
865859
// Token lifetime.
866860
if inputs.TokenLifetime == "" {
867861
defaultLifetime := strconv.Itoa(apiDefaultTokenLifetime)
@@ -1421,8 +1415,10 @@ func defaultPortForFramework(framework string) int {
14211415
return 8000
14221416
case "laravel":
14231417
return 8000
1424-
case "spring-boot", "java-ee", "vanilla-java", "jhipster":
1418+
case "java-ee", "vanilla-java", "jhipster":
14251419
return 8080
1420+
case "aspnet-mvc", "aspnet-owin":
1421+
return 5000
14261422
default:
14271423
return 3000
14281424
}
@@ -1440,6 +1436,17 @@ func validateAPIIdentifier(identifier string) error {
14401436
return nil
14411437
}
14421438

1439+
// randomSlugSuffix returns a lowercase hex string of the requested byte length
1440+
// (each byte expands to 2 hex chars). Falls back to a timestamp-based suffix if
1441+
// the crypto source fails, which should not happen in practice.
1442+
func randomSlugSuffix(n int) string {
1443+
b := make([]byte, n)
1444+
if _, err := rand.Read(b); err != nil {
1445+
return strconv.FormatInt(time.Now().UnixNano(), 36)
1446+
}
1447+
return hex.EncodeToString(b)
1448+
}
1449+
14431450
func generateClient(input SetupInputs, reqParams auth0.RequestParams) (*management.Client, error) {
14441451
if input.Name == "" {
14451452
input.Name = "My App"

internal/cli/quickstarts_test.go

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ func TestResolveRequestParams_AllQuickstartConfigs(t *testing.T) {
129129
[]string{"http://localhost:3000"}, "spa"},
130130
// Regular web: framework-specific callback paths.
131131
{"regular:nextjs:none", 3000,
132-
[]string{"http://localhost:3000/api/auth/callback"},
132+
[]string{"http://localhost:3000/auth/callback"},
133133
[]string{"http://localhost:3000"}, nil, "regular_web"},
134134
{"regular:fastify:none", 3000,
135135
[]string{"http://localhost:3000/auth/callback"},
@@ -141,7 +141,7 @@ func TestResolveRequestParams_AllQuickstartConfigs(t *testing.T) {
141141
[]string{"http://localhost:3000/callback"},
142142
[]string{"http://localhost:3000"}, nil, "regular_web"},
143143
{"regular:hono:none", 3000,
144-
[]string{"http://localhost:3000/callback"},
144+
[]string{"http://localhost:3000/auth/callback"},
145145
[]string{"http://localhost:3000"}, nil, "regular_web"},
146146
{"regular:vanilla-python:none", 3000,
147147
[]string{"http://localhost:3000/callback"},
@@ -166,12 +166,12 @@ func TestResolveRequestParams_AllQuickstartConfigs(t *testing.T) {
166166
{"regular:vanilla-go:none", 3000,
167167
[]string{"http://localhost:3000/callback"},
168168
[]string{"http://localhost:3000"}, nil, "regular_web"},
169-
{"regular:spring-boot:maven", 8080,
170-
[]string{"http://localhost:8080/login/oauth2/code/oidc"},
171-
[]string{"http://localhost:8080"}, nil, "regular_web"},
172-
{"regular:spring-boot:gradle", 8080,
173-
[]string{"http://localhost:8080/login/oauth2/code/oidc"},
174-
[]string{"http://localhost:8080"}, nil, "regular_web"},
169+
{"regular:spring-boot:maven", 3000,
170+
[]string{"http://localhost:3000/login/oauth2/code/okta"},
171+
[]string{"http://localhost:3000"}, nil, "regular_web"},
172+
{"regular:spring-boot:gradle", 3000,
173+
[]string{"http://localhost:3000/login/oauth2/code/okta"},
174+
[]string{"http://localhost:3000"}, nil, "regular_web"},
175175
{"regular:laravel:composer", 8000,
176176
[]string{"http://localhost:8000/callback"},
177177
[]string{"http://localhost:8000"}, nil, "regular_web"},
@@ -181,15 +181,15 @@ func TestResolveRequestParams_AllQuickstartConfigs(t *testing.T) {
181181
{"regular:jhipster:none", 8080,
182182
[]string{"http://localhost:8080/login/oauth2/code/oidc"},
183183
[]string{"http://localhost:8080"}, nil, "regular_web"},
184-
{"regular:aspnet-mvc:none", 3000,
185-
[]string{"http://localhost:3000/callback"},
186-
[]string{"http://localhost:3000"}, nil, "regular_web"},
184+
{"regular:aspnet-mvc:none", 5000,
185+
[]string{"http://localhost:5000/callback"},
186+
[]string{"http://localhost:5000"}, nil, "regular_web"},
187187
{"regular:aspnet-blazor:none", 3000,
188188
[]string{"http://localhost:3000/callback"},
189189
[]string{"http://localhost:3000"}, nil, "regular_web"},
190-
{"regular:aspnet-owin:none", 3000,
191-
[]string{"http://localhost:3000/callback"},
192-
[]string{"http://localhost:3000"}, nil, "regular_web"},
190+
{"regular:aspnet-owin:none", 5000,
191+
[]string{"http://localhost:5000/callback"},
192+
[]string{"http://localhost:5000"}, nil, "regular_web"},
193193
{"regular:vanilla-php:composer", 3000,
194194
[]string{"http://localhost:3000/callback"},
195195
[]string{"http://localhost:3000"}, nil, "regular_web"},
@@ -244,7 +244,7 @@ func TestResolveRequestParams_AllQuickstartConfigs(t *testing.T) {
244244
[]string{"http://localhost:8080"},
245245
[]string{"http://localhost:8080"}, "spa"},
246246
{"regular:nextjs:none", 8080,
247-
[]string{"http://localhost:8080/api/auth/callback"},
247+
[]string{"http://localhost:8080/auth/callback"},
248248
[]string{"http://localhost:8080"}, nil, "regular_web"},
249249
}
250250

@@ -476,11 +476,11 @@ func TestGenerateClient_AllQuickstartConfigs(t *testing.T) {
476476
{"spa:angular:none", 4200, "spa", 1, "http://localhost:4200", 1, 1},
477477
{"spa:flutter-web:none", 3000, "spa", 1, "http://localhost:3000", 1, 1},
478478
// Regular web: framework-specific paths.
479-
{"regular:nextjs:none", 3000, "regular_web", 1, "http://localhost:3000/api/auth/callback", 1, 0},
479+
{"regular:nextjs:none", 3000, "regular_web", 1, "http://localhost:3000/auth/callback", 1, 0},
480480
{"regular:fastify:none", 3000, "regular_web", 1, "http://localhost:3000/auth/callback", 1, 0},
481481
{"regular:nuxt:none", 3000, "regular_web", 1, "http://localhost:3000/auth/callback", 1, 0},
482482
{"regular:express:none", 3000, "regular_web", 1, "http://localhost:3000/callback", 1, 0},
483-
{"regular:hono:none", 3000, "regular_web", 1, "http://localhost:3000/callback", 1, 0},
483+
{"regular:hono:none", 3000, "regular_web", 1, "http://localhost:3000/auth/callback", 1, 0},
484484
{"regular:vanilla-python:none", 3000, "regular_web", 1, "http://localhost:3000/callback", 1, 0},
485485
// Flask detection sets port 5000 (Flask's historical default).
486486
{"regular:vanilla-python:none", 5000, "regular_web", 1, "http://localhost:5000/callback", 1, 0},
@@ -499,8 +499,8 @@ func TestGenerateClient_AllQuickstartConfigs(t *testing.T) {
499499
{"regular:vanilla-php:composer", 3000, "regular_web", 1, "http://localhost:3000/callback", 1, 0},
500500
{"regular:vanilla-java:maven", 8080, "regular_web", 1, "http://localhost:8080/callback", 1, 0},
501501
{"regular:java-ee:maven", 8080, "regular_web", 1, "http://localhost:8080/callback", 1, 0},
502-
{"regular:spring-boot:maven", 8080, "regular_web", 1, "http://localhost:8080/login/oauth2/code/oidc", 1, 0},
503-
{"regular:spring-boot:gradle", 8080, "regular_web", 1, "http://localhost:8080/login/oauth2/code/oidc", 1, 0},
502+
{"regular:spring-boot:maven", 8080, "regular_web", 1, "http://localhost:8080/login/oauth2/code/okta", 1, 0},
503+
{"regular:spring-boot:gradle", 8080, "regular_web", 1, "http://localhost:8080/login/oauth2/code/okta", 1, 0},
504504
// Native: static callback URLs appropriate per framework.
505505
// Flutter and React Native use bundle-ID-specific custom URI schemes; unknown at setup time.
506506
{"native:flutter:none", 0, "native", 0, "", 0, 0},

0 commit comments

Comments
 (0)