@@ -8,7 +8,7 @@ import type * as Scope from "effect/Scope";
88import * as HttpClient from "effect/http/HttpClient" ;
99
1010import { TailnetConnectionCredential } from "../connection/catalog.ts" ;
11- import type * as ConnectionCredentialStore from "../connection/credentialStore.ts" ;
11+ import * as ConnectionCredentialStore from "../connection/credentialStore.ts" ;
1212import { credentialMissingError , mapRemoteEnvironmentError } from "../connection/errors.ts" ;
1313import {
1414 ConnectionBlockedError ,
@@ -17,7 +17,7 @@ import {
1717} from "../connection/model.ts" ;
1818import { tailnetRefusalError } from "../connection/tailnet.ts" ;
1919import { environmentEndpointUrl } from "../environment/endpoint.ts" ;
20- import type { ManagedRelayDpopSigner } from "../relay/managedRelay.ts" ;
20+ import { ManagedRelayDpopSigner } from "../relay/managedRelay.ts" ;
2121import { executeEnvironmentHttpRequest , makeEnvironmentHttpApiGroupClient } from "../rpc/http.ts" ;
2222import { exchangeRemoteDpopAccessToken } from "./remote.ts" ;
2323
@@ -39,12 +39,12 @@ const proofError = () =>
3939export const signInOverTailnet = Effect . fn ( "clientRuntime.authorization.signInOverTailnet" ) (
4040 function * ( input : {
4141 readonly httpBaseUrl : string ;
42- readonly signer : ManagedRelayDpopSigner [ "Service" ] ;
4342 readonly clientMetadata ?: AuthClientPresentationMetadata ;
4443 } ) {
45- const thumbprint = yield * input . signer . thumbprint . pipe ( Effect . mapError ( proofError ) ) ;
44+ const signer = yield * ManagedRelayDpopSigner ;
45+ const thumbprint = yield * signer . thumbprint . pipe ( Effect . mapError ( proofError ) ) ;
4646 const signInUrl = environmentEndpointUrl ( input . httpBaseUrl , "/api/auth/tailnet" ) ;
47- const signInProof = yield * input . signer
47+ const signInProof = yield * signer
4848 . createProof ( { method : "POST" , url : signInUrl } )
4949 . pipe ( Effect . mapError ( proofError ) ) ;
5050 const client = yield * makeEnvironmentHttpApiGroupClient ( input . httpBaseUrl , "tailnet" ) ;
@@ -67,7 +67,7 @@ export const signInOverTailnet = Effect.fn("clientRuntime.authorization.signInOv
6767 answer . refused . suggestedGrant ,
6868 ) ;
6969 }
70- const tokenProof = yield * input . signer
70+ const tokenProof = yield * signer
7171 . createProof ( {
7272 method : "POST" ,
7373 url : environmentEndpointUrl ( input . httpBaseUrl , "/oauth/token" ) ,
@@ -94,67 +94,69 @@ export const signInOverTailnet = Effect.fn("clientRuntime.authorization.signInOv
9494 * again at its pinned address, before expiry or after a rejection, so every
9595 * host check runs again. Concurrent callers share one sign-in.
9696 */
97- export const makeTailnetSessions = ( input : {
98- readonly credentials : ConnectionCredentialStore . ConnectionCredentialStore [ "Service" ] ;
99- readonly signer : ManagedRelayDpopSigner [ "Service" ] ;
100- readonly httpClient : HttpClient . HttpClient ;
101- readonly clientMetadata : AuthClientPresentationMetadata ;
102- readonly scope : Scope . Scope ;
103- } ) => {
104- const renewals = new Map <
105- string ,
106- {
107- readonly id : object ;
108- readonly fiber : Fiber . Fiber < TailnetConnectionCredential , ConnectionAttemptError > ;
109- }
110- > ( ) ;
97+ export const makeTailnetSessions = Effect . fn ( "clientRuntime.authorization.makeTailnetSessions" ) (
98+ function * ( input : {
99+ readonly clientMetadata : AuthClientPresentationMetadata ;
100+ readonly scope : Scope . Scope ;
101+ } ) {
102+ const credentials = yield * ConnectionCredentialStore . ConnectionCredentialStore ;
103+ const signer = yield * ManagedRelayDpopSigner ;
104+ const httpClient = yield * HttpClient . HttpClient ;
105+ const renewals = new Map <
106+ string ,
107+ {
108+ readonly id : object ;
109+ readonly fiber : Fiber . Fiber < TailnetConnectionCredential , ConnectionAttemptError > ;
110+ }
111+ > ( ) ;
111112
112- const renew = Effect . fn ( "clientRuntime.authorization.tailnet.renew" ) ( function * (
113- credentialId : string ,
114- current : TailnetConnectionCredential ,
115- ) {
116- const pending = renewals . get ( credentialId ) ;
117- if ( pending !== undefined ) return yield * Fiber . join ( pending . fiber ) ;
118- const id = { } ;
119- const fiber = yield * signInOverTailnet ( {
120- httpBaseUrl : current . signInBaseUrl ,
121- signer : input . signer ,
122- clientMetadata : input . clientMetadata ,
123- } ) . pipe (
124- Effect . tap ( ( fresh ) => input . credentials . put ( credentialId , fresh ) ) ,
125- Effect . provideService ( HttpClient . HttpClient , input . httpClient ) ,
126- Effect . ensuring (
127- Effect . sync ( ( ) => {
128- if ( renewals . get ( credentialId ) ?. id === id ) renewals . delete ( credentialId ) ;
129- } ) ,
130- ) ,
131- // One caller's interruption must not cancel the renewal others wait on.
132- Effect . forkIn ( input . scope ) ,
133- ) ;
134- renewals . set ( credentialId , { id, fiber } ) ;
135- return yield * Fiber . join ( fiber ) ;
136- } ) ;
113+ const renew = Effect . fn ( "clientRuntime.authorization.tailnet.renew" ) ( function * (
114+ credentialId : string ,
115+ current : TailnetConnectionCredential ,
116+ ) {
117+ const pending = renewals . get ( credentialId ) ;
118+ if ( pending !== undefined ) return yield * Fiber . join ( pending . fiber ) ;
119+ const id = { } ;
120+ const fiber = yield * signInOverTailnet ( {
121+ httpBaseUrl : current . signInBaseUrl ,
122+ clientMetadata : input . clientMetadata ,
123+ } ) . pipe (
124+ Effect . tap ( ( fresh ) => credentials . put ( credentialId , fresh ) ) ,
125+ Effect . provideService ( ManagedRelayDpopSigner , signer ) ,
126+ Effect . provideService ( HttpClient . HttpClient , httpClient ) ,
127+ Effect . ensuring (
128+ Effect . sync ( ( ) => {
129+ if ( renewals . get ( credentialId ) ?. id === id ) renewals . delete ( credentialId ) ;
130+ } ) ,
131+ ) ,
132+ // One caller's interruption must not cancel the renewal others wait on.
133+ Effect . forkIn ( input . scope ) ,
134+ ) ;
135+ renewals . set ( credentialId , { id, fiber } ) ;
136+ return yield * Fiber . join ( fiber ) ;
137+ } ) ;
137138
138- /** The stored session, or a fresh one when it is missing a key match, near expiry, or rejected. */
139- const session = Effect . fn ( "clientRuntime.authorization.tailnet.session" ) ( function * (
140- credentialId : string ,
141- rejectedAccessToken ?: string ,
142- ) {
143- const stored = Option . getOrUndefined ( yield * input . credentials . get ( credentialId ) ) ;
144- if ( stored === undefined || ! isTailnetCredential ( stored ) ) {
145- return yield * credentialMissingError ( credentialId ) ;
146- }
147- const thumbprint = yield * input . signer . thumbprint . pipe ( Effect . mapError ( proofError ) ) ;
148- const now = yield * Clock . currentTimeMillis ;
149- if (
150- stored . dpopThumbprint === thumbprint &&
151- stored . expiresAtEpochMs > now + DPOP_ACCESS_TOKEN_REFRESH_SKEW_MS &&
152- stored . accessToken !== rejectedAccessToken
139+ /** The stored session, or a fresh one when it is missing a key match, near expiry, or rejected. */
140+ const session = Effect . fn ( "clientRuntime.authorization.tailnet.session" ) ( function * (
141+ credentialId : string ,
142+ rejectedAccessToken ?: string ,
153143 ) {
154- return stored ;
155- }
156- return yield * renew ( credentialId , stored ) ;
157- } ) ;
144+ const stored = Option . getOrUndefined ( yield * credentials . get ( credentialId ) ) ;
145+ if ( stored === undefined || ! isTailnetCredential ( stored ) ) {
146+ return yield * credentialMissingError ( credentialId ) ;
147+ }
148+ const thumbprint = yield * signer . thumbprint . pipe ( Effect . mapError ( proofError ) ) ;
149+ const now = yield * Clock . currentTimeMillis ;
150+ if (
151+ stored . dpopThumbprint === thumbprint &&
152+ stored . expiresAtEpochMs > now + DPOP_ACCESS_TOKEN_REFRESH_SKEW_MS &&
153+ stored . accessToken !== rejectedAccessToken
154+ ) {
155+ return stored ;
156+ }
157+ return yield * renew ( credentialId , stored ) ;
158+ } ) ;
158159
159- return { session } ;
160- } ;
160+ return { session } ;
161+ } ,
162+ ) ;
0 commit comments