11package org.radarbase.authorizer.service
22
3+ import io.ktor.client.call.body
34import io.ktor.client.request.basicAuth
45import io.ktor.client.request.forms.submitForm
6+ import io.ktor.client.request.get
7+ import io.ktor.client.request.url
58import io.ktor.client.statement.bodyAsText
69import io.ktor.http.isSuccess
710import io.ktor.http.takeFrom
811import jakarta.ws.rs.core.Context
912import kotlinx.coroutines.Dispatchers
1013import kotlinx.coroutines.withContext
14+ import org.radarbase.authorizer.api.OuraAuthUserId
15+ import org.radarbase.authorizer.api.RequestTokenPayload
16+ import org.radarbase.authorizer.api.RestOauth2AccessToken
1117import org.radarbase.authorizer.config.AuthorizerConfig
1218import org.radarbase.authorizer.doa.entity.RestSourceUser
19+ import org.radarbase.jersey.exception.HttpBadGatewayException
20+ import java.io.IOException
1321
1422class OuraAuthorizationService (
1523 @Context private val clients : RestSourceClientService ,
1624 @Context private val config : AuthorizerConfig ,
1725) : OAuth2RestSourceAuthorizationService(clients, config) {
26+ override suspend fun requestAccessToken (payload : RequestTokenPayload , sourceType : String ): RestOauth2AccessToken {
27+ val accessToken: RestOauth2AccessToken = super .requestAccessToken(payload, sourceType)
28+ return accessToken.copy(externalUserId = getExternalId(accessToken.accessToken))
29+ }
1830
1931 override suspend fun revokeToken (user : RestSourceUser ): Boolean {
2032 val accessToken = user.accessToken ? : run {
@@ -23,7 +35,7 @@ class OuraAuthorizationService(
2335 }
2436 // revoke token using the deregistrationEndpoint token endpoint
2537 val authConfig = clients.forSourceType(user.sourceType)
26- val deregistrationEndpoint = checkNotNull(authConfig.deregistrationEndpoint)
38+ val deregistrationEndpoint = checkNotNull(authConfig.deregistrationEndpoint) { " Missing Oura deregistration endpoint configuration " }
2739
2840 val isSuccess = try {
2941 withContext(Dispatchers .IO ) {
@@ -61,4 +73,31 @@ class OuraAuthorizationService(
6173 false
6274 }
6375 }
76+
77+ private suspend fun getExternalId (accessToken : String ): String = withContext(Dispatchers .IO ) {
78+ try {
79+ val response = httpClient.get {
80+ url(OURA_USER_ID_ENDPOINT ) {
81+ parameters.append(" access_token" , accessToken)
82+ }
83+ }
84+ if (response.status.isSuccess()) {
85+ response.body<OuraAuthUserId >().userId
86+ } else {
87+ logger.error(
88+ " Unable to fetch data from Oura $OURA_USER_ID_ENDPOINT (Http Status {}): {}" ,
89+ response.status,
90+ response.bodyAsText().take(512 ),
91+ )
92+ throw HttpBadGatewayException (" Cannot connect to $OURA_USER_ID_ENDPOINT : HTTP status ${response.status} " )
93+ }
94+ } catch (ex: IOException ) {
95+ logger.error(" Unable to fetch data from Oura $OURA_USER_ID_ENDPOINT : {}" , ex.toString())
96+ throw HttpBadGatewayException (" Cannot connect to $OURA_USER_ID_ENDPOINT : I/O error" )
97+ }
98+ }
99+
100+ companion object {
101+ private const val OURA_USER_ID_ENDPOINT = " https://api.ouraring.com/v1/userinfo"
102+ }
64103}
0 commit comments