Add anonymous OOBE gateway grants
CI / verify (push) Has been cancelled
CI / publish (push) Has been cancelled

Provide App Attest-bound, one-time onboarding AI access without creating accounts, with durable replay protection and production deployment safeguards.
This commit is contained in:
Rocky
2026-08-21 22:55:46 +08:00
parent edd0d9feca
commit 0d236f57fb
32 changed files with 2202 additions and 61 deletions
@@ -131,6 +131,11 @@ import com.osglab.account.features.inviteweb.InviteWebConfig
import com.osglab.account.features.inviteweb.InviteOpenRecorder
import com.osglab.account.features.inviteweb.ReferralLookupPort
import com.osglab.account.features.inviteweb.configureInviteWebRoutes
import com.osglab.account.features.oobe.ExposedOobeRepository
import com.osglab.account.features.oobe.OobeGrantService
import com.osglab.account.features.oobe.OobeRepository
import com.osglab.account.features.oobe.OobeTokenSettings
import com.osglab.account.features.oobe.oobeRoutes
import com.osglab.account.features.referrals.routes.referralRoutes
import com.osglab.account.features.referrals.services.ReferralOperations
import com.osglab.account.features.referrals.services.ReferralService
@@ -329,6 +334,7 @@ fun Application.module() {
healthRoutes(koin.get())
rateLimit(AUTH_RATE_LIMIT) {
authRoutes(koin.get())
oobeRoutes(koin.get())
}
rateLimit(ACCOUNT_RATE_LIMIT) {
accountRoutes(koin.get())
@@ -652,8 +658,27 @@ fun accountServerModule(config: AppConfig): Module = module {
maximumGrantLifetime = Duration.ofDays(config.session.gatewayGrantDays),
)
}
single<OobeRepository> { ExposedOobeRepository(get()) }
single {
OobeTokenSettings(
issuer = config.session.issuer,
audience = "${config.session.audience}-gateway",
accessTokenHmacSecret = deriveGatewaySecret(
config.session.hmacSecret,
"oobe-gateway-access",
),
refreshTokenHmacSecret = deriveGatewaySecret(
config.session.hmacSecret,
"oobe-gateway-refresh",
),
)
}
single { OobeGrantService(get(), get(), get()) }
single { GatewayGrantService(get(), get()) }
single<GatewayAccessTokenPort> { GatewayBearerIdentity(get()) }
single<GatewayAccessTokenPort> {
val oobeGrants = get<OobeGrantService>()
GatewayBearerIdentity(get(), oobeGrants::authenticate)
}
single<CreditReservationPort> {
CreditReservationAdapter(
creditService = get(),
@@ -664,7 +689,7 @@ fun accountServerModule(config: AppConfig): Module = module {
single {
ProviderCatalog(configuredProviders(config, get()))
}
single { GatewayService(get(), get(), get(), get(), get()) }
single { GatewayService(get(), get(), get(), get(), get(), get()) }
single { GatewayReconciliationService(get(), get()) }
single {
InviteWebConfig(