@@ -1192,3 +1192,113 @@ func TestAsRedirectURL(t *testing.T) {
11921192 require .Contains (t , fragment , "sb" , "Fragment should contain Supabase Auth identifier 'sb'" )
11931193 require .Equal (t , "" , fragment .Get ("sb" ), "Supabase Auth identifier should have empty value" )
11941194}
1195+
1196+ func TestGenerateAccessTokenAllowLowAAL (t * testing.T ) {
1197+ config , err := conf .LoadGlobal ("../../hack/test.env" )
1198+ require .NoError (t , err )
1199+
1200+ conn , err := test .SetupDBConnection (config )
1201+ require .NoError (t , err )
1202+ defer conn .Close ()
1203+
1204+ allowLowAAL := 5 * time .Minute
1205+
1206+ req , err := http .NewRequest ("POST" , "https://example.com/" , nil )
1207+ require .NoError (t , err )
1208+
1209+ now := time .Now ().UTC ().Truncate (time .Second )
1210+
1211+ t .Run ("AAL1 session for MFA user uses AllowLowAAL expiry" , func (t * testing.T ) {
1212+ models .TruncateAll (conn )
1213+
1214+ u , err := models .NewUser ("" , "test@example.com" , "password" , "authenticated" , nil )
1215+ require .NoError (t , err )
1216+ require .NoError (t , conn .Create (u ))
1217+
1218+ // Add a verified TOTP factor so HighestPossibleAAL() returns AAL2
1219+ factor := models .NewFactor (u , "my-totp" , models .TOTP , models .FactorStateVerified )
1220+ require .NoError (t , conn .Create (factor ))
1221+ require .NoError (t , conn .Eager ().Find (u , u .ID ))
1222+
1223+ session , err := models .NewSession (u .ID , nil )
1224+ require .NoError (t , err )
1225+ // Session stays at AAL1 (default)
1226+ require .NoError (t , conn .Create (session ))
1227+
1228+ cfg := * config
1229+ cfg .Sessions .AllowLowAAL = & allowLowAAL
1230+
1231+ srv := NewService (& cfg , & panicHookManager {})
1232+ srv .SetTimeFunc (func () time.Time { return now })
1233+
1234+ _ , expiresAt , err := srv .GenerateAccessToken (req , conn , GenerateAccessTokenParams {
1235+ User : u ,
1236+ SessionID : & session .ID ,
1237+ AuthenticationMethod : models .PasswordGrant ,
1238+ })
1239+ require .NoError (t , err )
1240+ require .Equal (t , now .Add (allowLowAAL ).Unix (), expiresAt )
1241+ })
1242+
1243+ t .Run ("AAL2 session for MFA user uses standard JWT expiry" , func (t * testing.T ) {
1244+ models .TruncateAll (conn )
1245+
1246+ u , err := models .NewUser ("" , "test2@example.com" , "password" , "authenticated" , nil )
1247+ require .NoError (t , err )
1248+ require .NoError (t , conn .Create (u ))
1249+
1250+ factor := models .NewFactor (u , "my-totp" , models .TOTP , models .FactorStateVerified )
1251+ require .NoError (t , conn .Create (factor ))
1252+ require .NoError (t , conn .Eager ().Find (u , u .ID ))
1253+
1254+ session , err := models .NewSession (u .ID , & factor .ID )
1255+ require .NoError (t , err )
1256+ aal2 := models .AAL2 .String ()
1257+ session .AAL = & aal2
1258+ require .NoError (t , conn .Create (session ))
1259+
1260+ cfg := * config
1261+ cfg .Sessions .AllowLowAAL = & allowLowAAL
1262+
1263+ srv := NewService (& cfg , & panicHookManager {})
1264+ srv .SetTimeFunc (func () time.Time { return now })
1265+
1266+ _ , expiresAt , err := srv .GenerateAccessToken (req , conn , GenerateAccessTokenParams {
1267+ User : u ,
1268+ SessionID : & session .ID ,
1269+ AuthenticationMethod : models .PasswordGrant ,
1270+ })
1271+ require .NoError (t , err )
1272+ require .Equal (t , now .Add (time .Second * time .Duration (config .JWT .Exp )).Unix (), expiresAt )
1273+ })
1274+
1275+ t .Run ("AAL1 session without AllowLowAAL uses standard JWT expiry" , func (t * testing.T ) {
1276+ models .TruncateAll (conn )
1277+
1278+ u , err := models .NewUser ("" , "test3@example.com" , "password" , "authenticated" , nil )
1279+ require .NoError (t , err )
1280+ require .NoError (t , conn .Create (u ))
1281+
1282+ factor := models .NewFactor (u , "my-totp" , models .TOTP , models .FactorStateVerified )
1283+ require .NoError (t , conn .Create (factor ))
1284+ require .NoError (t , conn .Eager ().Find (u , u .ID ))
1285+
1286+ session , err := models .NewSession (u .ID , nil )
1287+ require .NoError (t , err )
1288+ require .NoError (t , conn .Create (session ))
1289+
1290+ cfg := * config
1291+ cfg .Sessions .AllowLowAAL = nil
1292+
1293+ srv := NewService (& cfg , & panicHookManager {})
1294+ srv .SetTimeFunc (func () time.Time { return now })
1295+
1296+ _ , expiresAt , err := srv .GenerateAccessToken (req , conn , GenerateAccessTokenParams {
1297+ User : u ,
1298+ SessionID : & session .ID ,
1299+ AuthenticationMethod : models .PasswordGrant ,
1300+ })
1301+ require .NoError (t , err )
1302+ require .Equal (t , now .Add (time .Second * time .Duration (config .JWT .Exp )).Unix (), expiresAt )
1303+ })
1304+ }
0 commit comments