@@ -82,8 +82,14 @@ export class OAuthController extends MembershipBaseController {
8282 return this . handleDeviceCodeGrant ( device_code , client_id , res ) ;
8383 }
8484
85- // All other grant types require client_secret validation
86- const client = ( await this . repos . oAuthClient . loadByClientIdAndSecret ( client_id , client_secret ) ) as any ;
85+ // Validate client: require client_secret for all grant types except refresh_token
86+ // (public clients like device-flow TV apps may not have a client_secret)
87+ let client ;
88+ if ( grant_type === "refresh_token" && ! client_secret ) {
89+ client = ( await this . repos . oAuthClient . loadByClientId ( client_id ) ) as any ;
90+ } else {
91+ client = ( await this . repos . oAuthClient . loadByClientIdAndSecret ( client_id , client_secret ) ) as any ;
92+ }
8793 if ( ! client ) return this . json ( { error : "invalid_client" } , 400 ) ;
8894
8995 if ( grant_type === "authorization_code" ) {
@@ -120,14 +126,14 @@ export class OAuthController extends MembershipBaseController {
120126 await UserHelper . replaceDomainAdminPermissions ( [ loginUserChurch ] ) ;
121127 UserHelper . addAllReportingPermissions ( [ loginUserChurch ] ) ;
122128
123- // Create access token
129+ // Create access token (refresh token expires in 90 days)
124130 const token : OAuthToken = {
125131 clientId : client . clientId ,
126132 userChurchId : authCode . userChurchId ,
127- accessToken : AuthenticatedUser . getCombinedApiJwt ( user , loginUserChurch ) ,
133+ accessToken : AuthenticatedUser . getCombinedApiJwt ( user , loginUserChurch , "7 days" ) ,
128134 refreshToken : UniqueIdHelper . shortId ( ) ,
129135 scopes : authCode . scopes ,
130- expiresAt : new Date ( Date . now ( ) + 60 * 60 * 1000 * 12 ) // 12 hours
136+ expiresAt : new Date ( Date . now ( ) + 90 * 24 * 60 * 60 * 1000 ) // 90 days
131137 } ;
132138 await this . repos . oAuthToken . save ( token ) ;
133139
@@ -137,7 +143,7 @@ export class OAuthController extends MembershipBaseController {
137143 return this . json ( {
138144 access_token : token . accessToken ,
139145 token_type : "Bearer" ,
140- expires_in : 3600 * 12 ,
146+ expires_in : 7 * 24 * 3600 , // 7 days (matches JWT expiration)
141147 created_at : Math . floor ( Date . now ( ) / 1000 ) ,
142148 refresh_token : token . refreshToken ,
143149 scope : token . scopes
@@ -172,14 +178,14 @@ export class OAuthController extends MembershipBaseController {
172178 await UserHelper . replaceDomainAdminPermissions ( [ loginUserChurch ] ) ;
173179 UserHelper . addAllReportingPermissions ( [ loginUserChurch ] ) ;
174180
175- // Create new access token with proper JWT
181+ // Create new access token (refresh token expires in 90 days)
176182 const token : OAuthToken = {
177183 clientId : client . clientId ,
178184 userChurchId : oldToken . userChurchId ,
179- accessToken : AuthenticatedUser . getCombinedApiJwt ( user , loginUserChurch ) ,
185+ accessToken : AuthenticatedUser . getCombinedApiJwt ( user , loginUserChurch , "7 days" ) ,
180186 refreshToken : UniqueIdHelper . shortId ( ) ,
181187 scopes : oldToken . scopes ,
182- expiresAt : new Date ( Date . now ( ) + 60 * 60 * 1000 * 12 ) // 12 hours
188+ expiresAt : new Date ( Date . now ( ) + 90 * 24 * 60 * 60 * 1000 ) // 90 days
183189 } ;
184190 await this . repos . oAuthToken . save ( token ) ;
185191
@@ -189,7 +195,7 @@ export class OAuthController extends MembershipBaseController {
189195 access_token : token . accessToken ,
190196 token_type : "Bearer" ,
191197 created_at : Math . floor ( Date . now ( ) / 1000 ) ,
192- expires_in : 3600 * 12 ,
198+ expires_in : 7 * 24 * 3600 , // 7 days (matches JWT expiration)
193199 refresh_token : token . refreshToken ,
194200 scope : token . scopes
195201 } ) ;
@@ -310,17 +316,17 @@ export class OAuthController extends MembershipBaseController {
310316 UserHelper . addAllReportingPermissions ( [ loginUserChurch ] ) ;
311317
312318 // Create access token
313- const accessToken = AuthenticatedUser . getCombinedApiJwt ( user , loginUserChurch ) ;
319+ const accessToken = AuthenticatedUser . getCombinedApiJwt ( user , loginUserChurch , "7 days" ) ;
314320 const refreshToken = UniqueIdHelper . shortId ( ) ;
315321
316- // Store the refresh token for later use
322+ // Store the refresh token (expires in 90 days)
317323 const token : OAuthToken = {
318324 clientId : dc . clientId ,
319325 userChurchId : dc . userChurchId ,
320326 accessToken,
321327 refreshToken,
322328 scopes : dc . scopes ,
323- expiresAt : new Date ( Date . now ( ) + 60 * 60 * 1000 * 12 ) // 12 hours
329+ expiresAt : new Date ( Date . now ( ) + 90 * 24 * 60 * 60 * 1000 ) // 90 days
324330 } ;
325331 await this . repos . oAuthToken . save ( token ) ;
326332
@@ -330,7 +336,7 @@ export class OAuthController extends MembershipBaseController {
330336 return this . json ( {
331337 access_token : accessToken ,
332338 token_type : "Bearer" ,
333- expires_in : 3600 * 12 , // 12 hours
339+ expires_in : 7 * 24 * 3600 , // 7 days (matches JWT expiration)
334340 refresh_token : refreshToken ,
335341 scope : dc . scopes
336342 } ) ;
0 commit comments