From 82b8618553329241529809be338e14ad12df48a2 Mon Sep 17 00:00:00 2001 From: Pine Date: Mon, 31 Mar 2025 14:44:15 -0600 Subject: [PATCH 1/5] Update api key reading --- cli/src/commands/dev/index.ts | 4 ++-- cli/src/util/hypermode.ts | 8 +++---- runtime/secrets/secrets.go | 45 ++++------------------------------- 3 files changed, 11 insertions(+), 46 deletions(-) diff --git a/cli/src/commands/dev/index.ts b/cli/src/commands/dev/index.ts index 558264130..55dd56110 100644 --- a/cli/src/commands/dev/index.ts +++ b/cli/src/commands/dev/index.ts @@ -164,8 +164,8 @@ export default class DevCommand extends BaseCommand { ...process.env, MODUS_ENV: "dev", HYP_EMAIL: hypSettings.email, - HYP_JWT: hypSettings.jwt, - HYP_ORG_ID: hypSettings.orgId, + HYP_API_KEY: hypSettings.apiKey, + HYP_WORKSPACE_ID: hypSettings.workspaceId, }; // Spawn the runtime child process diff --git a/cli/src/util/hypermode.ts b/cli/src/util/hypermode.ts index 742237fac..1fbf6bfff 100644 --- a/cli/src/util/hypermode.ts +++ b/cli/src/util/hypermode.ts @@ -14,8 +14,8 @@ import chalk from "chalk"; type HypSettings = { email?: string; - jwt?: string; - orgId?: string; + apiKey?: string; + workspaceId?: string; }; export async function readHypermodeSettings(): Promise { @@ -28,8 +28,8 @@ export async function readHypermodeSettings(): Promise { const settings = JSON.parse(await fs.readFile(path, "utf-8")); return { email: settings.HYP_EMAIL, - jwt: settings.HYP_JWT, - orgId: settings.HYP_ORG_ID, + apiKey: settings.HYP_API_KEY, + workspaceId: settings.HYP_WORKSPACE_ID, }; } catch (e) { console.warn(chalk.yellow("Error reading " + path), e); diff --git a/runtime/secrets/secrets.go b/runtime/secrets/secrets.go index 3e06b926e..672b80f32 100644 --- a/runtime/secrets/secrets.go +++ b/runtime/secrets/secrets.go @@ -16,10 +16,8 @@ import ( "net/http" "os" "regexp" - "time" "github.com/fatih/color" - "github.com/golang-jwt/jwt/v5" "github.com/hypermodeinc/modus/lib/manifest" "github.com/hypermodeinc/modus/runtime/logger" "github.com/hypermodeinc/modus/runtime/utils" @@ -94,57 +92,24 @@ func ApplySecretsToHttpRequest(ctx context.Context, connection *manifest.HTTPCon func ApplyAuthToLocalHypermodeModelRequest(ctx context.Context, connection manifest.ConnectionInfo, req *http.Request) error { - jwt := os.Getenv("HYP_JWT") - orgId := os.Getenv("HYP_ORG_ID") + apiKey := os.Getenv("HYP_API_KEY") + workspaceId := os.Getenv("HYP_WORKSPACE_ID") warningColor := color.New(color.FgHiYellow, color.Bold) - if jwt == "" || orgId == "" { + if apiKey == "" || workspaceId == "" { fmt.Fprintln(os.Stderr) warningColor.Fprintln(os.Stderr, "Warning: Local authentication not found. Please login using `hyp login`") fmt.Fprintln(os.Stderr) return errLocalAuthFailed } - isExpired, err := checkJWTExpiration(jwt) - if err != nil { - return err - } - if isExpired { - fmt.Fprintln(os.Stderr) - warningColor.Fprintln(os.Stderr, "Warning: Local authentication expired. Please login using `hyp login`") - fmt.Fprintln(os.Stderr) - return errLocalAuthFailed - } - - req.Header.Set("Authorization", "Bearer "+jwt) - req.Header.Set("HYP-ORG-ID", orgId) + req.Header.Set("Authorization", "Bearer "+apiKey) + req.Header.Set("HYP-WORKSPACE-ID", workspaceId) return nil } -// checkJWTExpiration checks if the JWT has expired based on the 'exp' claim. -func checkJWTExpiration(tokenString string) (bool, error) { - p := jwt.Parser{} - token, _, err := p.ParseUnverified(tokenString, jwt.MapClaims{}) - if err != nil { - return false, fmt.Errorf("failed to parse: %w", err) - } - - claims, ok := token.Claims.(jwt.MapClaims) - if !ok { - return false, fmt.Errorf("failed to extract claims from JWT") - } - - exp, ok := claims["exp"].(float64) - if !ok { - return false, fmt.Errorf("exp claim is missing or not a number") - } - - expirationTime := time.Unix(int64(exp), 0) - return time.Now().After(expirationTime), nil -} - // ApplySecretsToString evaluates the given string and replaces any placeholders // present in the string with their secret values for the given connection. func ApplySecretsToString(ctx context.Context, connection manifest.ConnectionInfo, str string) (string, error) { From e71732986aa29fc91473044603e7a510a8621012 Mon Sep 17 00:00:00 2001 From: Pine Date: Mon, 31 Mar 2025 14:47:53 -0600 Subject: [PATCH 2/5] changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 47d5fa170..ef0fd0f44 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ # Change Log +## 2025-03-29 - Runtime 0.17.8 + +- fix: Update Hypermode api key reading for modus local dev [#805](https://github.com/hypermodeinc/modus/pull/805) + ## 2025-03-28 - Runtime 0.17.7 - feat: support new Dgraph connection string format [#803](https://github.com/hypermodeinc/modus/pull/803) From d42d0982214f8851b8a8b8a985916d48aa612709 Mon Sep 17 00:00:00 2001 From: Pine Date: Tue, 1 Apr 2025 10:43:43 -0600 Subject: [PATCH 3/5] update error handling for invalid/expired API key or quota reached --- runtime/models/models.go | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/runtime/models/models.go b/runtime/models/models.go index a28bb7985..2274dda4c 100644 --- a/runtime/models/models.go +++ b/runtime/models/models.go @@ -93,8 +93,17 @@ func PostToModelEndpoint[TResult any](ctx context.Context, model *manifest.Model var empty TResult var httpe *utils.HttpError if errors.As(err, &httpe) { - if app.IsDevEnvironment() && httpe.StatusCode == http.StatusNotFound { - return empty, fmt.Errorf("model %s is not available in the local dev environment", model.SourceModel) + switch httpe.StatusCode { + case http.StatusNotFound: + if app.IsDevEnvironment() { + return empty, fmt.Errorf("model %s is not available in the local dev environment", model.SourceModel) + } + case http.StatusUnauthorized: + return empty, fmt.Errorf("invalid or expired API key") + case http.StatusForbidden: + return empty, fmt.Errorf("API key is disabled or usage limit exceeded") + case http.StatusTooManyRequests: + return empty, fmt.Errorf("rate limit exceeded") } } From 0d8344db3149e7c2a8127cf85d3a56f2381c67ed Mon Sep 17 00:00:00 2001 From: Pine Date: Tue, 1 Apr 2025 11:09:37 -0600 Subject: [PATCH 4/5] update error message and changelog --- CHANGELOG.md | 6 +++++- runtime/models/models.go | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ef0fd0f44..c81741adb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,11 @@ # Change Log -## 2025-03-29 - Runtime 0.17.8 +## 2025-04-01 - Runtime 0.17.8 + +- fix: Update Hypermode api key reading for modus local dev [#805](https://github.com/hypermodeinc/modus/pull/805) + +## 2025-04-01 - CLI 0.17.8 - fix: Update Hypermode api key reading for modus local dev [#805](https://github.com/hypermodeinc/modus/pull/805) diff --git a/runtime/models/models.go b/runtime/models/models.go index 2274dda4c..c7825ec1e 100644 --- a/runtime/models/models.go +++ b/runtime/models/models.go @@ -99,7 +99,7 @@ func PostToModelEndpoint[TResult any](ctx context.Context, model *manifest.Model return empty, fmt.Errorf("model %s is not available in the local dev environment", model.SourceModel) } case http.StatusUnauthorized: - return empty, fmt.Errorf("invalid or expired API key") + return empty, fmt.Errorf("invalid or expired API key. Please use `hyp login` to create a new API key") case http.StatusForbidden: return empty, fmt.Errorf("API key is disabled or usage limit exceeded") case http.StatusTooManyRequests: From 0ee77b28c0da8cfa9c43eaa3289b7e1b392e616b Mon Sep 17 00:00:00 2001 From: Pine Date: Tue, 1 Apr 2025 11:21:50 -0600 Subject: [PATCH 5/5] update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c81741adb..181a558a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ - fix: Update Hypermode api key reading for modus local dev [#805](https://github.com/hypermodeinc/modus/pull/805) -## 2025-04-01 - CLI 0.17.8 +## 2025-04-01 - CLI 0.17.2 - fix: Update Hypermode api key reading for modus local dev [#805](https://github.com/hypermodeinc/modus/pull/805)