Merge pull request #1734 from entireio/fix/auth-token-jurisdiction-context · Entire

Merge pull request #1734 from entireio/fix/auth-token-jurisdiction-context

b683403→main·

jagregory·4d ago·2 files·+150 added/-59 removed

fix(auth): auth token --jurisdiction should follow the active context

Changes

2

16 unmodified lines

17
18
19
20
21
22
23
239 unmodified lines

263
264
265
265
266
267
268
269
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
274
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
25 unmodified lines

343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
307
308
309
310
311
364
365
366
367
368
313
369
370
315
371
372
373
319
374
375
376
377
323
378
379
325
326
327
328
329
330
331
380
381
382
383

16 unmodified lines

"github.com/entireio/cli/cmd/entire/cli/api"
    "github.com/entireio/cli/internal/entireclient/clusterdiscovery"
    "github.com/entireio/cli/internal/entireclient/contexts"
    "github.com/entireio/cli/internal/entireclient/httputil"
    "github.com/entireio/cli/internal/entireclient/userdirs"
)
239 unmodified lines

httpClient     *http.Client
}

// resolveCellSubject picks the jurisdiction-exchange subject: ENTIRE_TOKEN when
// set (exclusive, fail-closed), otherwise the active stored login context. This
// is the ENTIRE_TOKEN-aware dispatcher used by JurisdictionToken;
// NewEntireAPICellClient calls resolveStoredCellSubject directly so its behavior
// is unchanged.
// resolveCellSubject picks the jurisdiction-exchange subject for
// JurisdictionToken (the `entire auth token --jurisdiction` scripting helper):
// ENTIRE_TOKEN when set (exclusive, fail-closed), otherwise the ACTIVE stored
// login context.

// It deliberately uses the active context — the same login `entire auth token`
// (no flag) prints a bearer for — rather than resolveStoredCellSubject's
// data-host discovery. `--jurisdiction` mints a token for the caller's SELECTED
// environment, so with (say) a partial.to context active it must mint a
// partial.to token even though the data host defaults to entire.io. Discovery
// keys off api.BaseURL() and would pick whichever context that host trusts,
// silently ignoring the selection. NewEntireAPICellClient is a different case —
// it dials the data plane — so it keeps calling resolveStoredCellSubject.
func resolveCellSubject(ctx context.Context, insecureHTTP bool) (cellSubject, error) {
    if raw, ok := os.LookupEnv(EnvTokenVar); ok {
        return resolveEnvTokenCellSubject(raw, insecureHTTP)
    }
    return resolveStoredCellSubject(ctx, insecureHTTP)
    return resolveActiveContextCellSubject(ctx, insecureHTTP)
}

// resolveActiveContextCellSubject builds the exchange subject from the active
// stored login context: it refreshes that context's login JWT and uses the
// context's own core as both the environment signal (dataOrigin) and the
// exchange target. See resolveCellSubject for why `--jurisdiction` follows the
// active context instead of discovering one against the data host.
func resolveActiveContextCellSubject(ctx context.Context, insecureHTTP bool) (cellSubject, error) {
    if insecureHTTP {
        EnableInsecureHTTP()
    }
    c, ok, err := activeContext()
    if err != nil {
        return cellSubject{}, err
    }
    if !ok {
        return cellSubject{}, fmt.Errorf("not logged in (run 'entire login' first): %w", ErrNotLoggedIn)
    }

loginJWT, err := refreshCellLoginJWT(ctx, c)
    if err != nil {
        return cellSubject{}, err
    }

origin := api.OriginOnly(c.CoreURL)
    return cellSubject{
        loginJWT:       loginJWT,
        discoveredCore: origin,
        dataOrigin:     origin,
        httpClient:     cellExchangeHTTPClient(origin),
    }, nil
}

// resolveStoredCellSubject resolves the exchange subject from the active stored
25 unmodified lines

return cellSubject{}, err
    }

loginJWT, err := refreshCellLoginJWT(ctx, selected)
    if err != nil {
        return cellSubject{}, err
    }

return cellSubject{
        loginJWT:       loginJWT,
        discoveredCore: selected.CoreURL,
        dataOrigin:     dataOrigin,
        httpClient:     httpClient,
    }, nil
}

// refreshCellLoginJWT returns c's login JWT, transparently re-minting it from the
// stored refresh token. Shared by the active-context and discovered-context cell
// subject resolvers, which differ only in how they pick c.
func refreshCellLoginJWT(ctx context.Context, c *contexts.Context) (string, error) {
    // Gate the login provider's HTTPS relaxation on the core it actually dials
    // (selected.CoreURL) plus the explicit --insecure-http-auth opt-in, matching
    // the sibling ResolveDataAPIToken. A loopback data API must not relax HTTPS
    // for a non-loopback core.
    allowInsecure := insecureHTTPEnabled() || isLoopbackHTTP(selected.CoreURL)
    loginProvider, err := NewRefreshingLoginProvider(selected, cellExchangeTransportForTest, allowInsecure)
    // plus the explicit --insecure-http-auth opt-in: a loopback core must not
    // relax HTTPS for a non-loopback one.
    allowInsecure := insecureHTTPEnabled() || isLoopbackHTTP(c.CoreURL)
    loginProvider, err := NewRefreshingLoginProvider(c, cellExchangeTransportForTest, allowInsecure)
    if err != nil {
        return cellSubject{}, err
        return "", err
    }

loginJWT, err := loginProvider(ctx)
    if err != nil {
        if errors.Is(err, ErrNotLoggedIn) {
            return cellSubject{}, fmt.Errorf("not logged in (run 'entire login' first): %w", err)
            return "", fmt.Errorf("not logged in (run 'entire login' first): %w", err)
        }
        // The provider already prefixes "refresh login token:"; return as-is to
        // avoid a doubled prefix.
        return cellSubject{}, err
        return "", err
    }

return cellSubject{
        loginJWT:       loginJWT,
        discoveredCore: selected.CoreURL,
        dataOrigin:     dataOrigin,
        httpClient:     httpClient,
    }, nil
    return loginJWT, nil
}

// resolveEnvTokenCellSubject builds the exchange subject from ENTIRE_TOKEN: the
``