Skip to content

Commit 236bc4f

Browse files
feat: enhance DPoP context tests and add edge case handling in middleware
1 parent 1805e5a commit 236bc4f

File tree

3 files changed

+587
-3
lines changed

3 files changed

+587
-3
lines changed

core/dpop_context_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ func TestDPoPContext_Helpers(t *testing.T) {
4444
})
4545

4646
t.Run("GetDPoPContext returns nil when wrong type", func(t *testing.T) {
47-
ctx := context.WithValue(context.Background(), testContextKey("wrong"), "wrong-type")
47+
ctx := context.WithValue(context.Background(), dpopContextKey, "wrong-type")
4848
retrieved := GetDPoPContext(ctx)
4949
assert.Nil(t, retrieved)
5050
})
@@ -67,7 +67,7 @@ func TestDPoPContext_Helpers(t *testing.T) {
6767
})
6868

6969
t.Run("HasDPoPContext returns false when wrong type", func(t *testing.T) {
70-
ctx := context.WithValue(context.Background(), testContextKey("wrong"), "wrong-type")
71-
assert.False(t, HasDPoPContext(ctx))
70+
ctx := context.WithValue(context.Background(), dpopContextKey, "wrong-type")
71+
assert.True(t, HasDPoPContext(ctx)) // HasDPoPContext only checks key existence
7272
})
7373
}

core/dpop_test.go

Lines changed: 293 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1067,3 +1067,296 @@ func TestCheckTokenWithDPoP_EdgeCases(t *testing.T) {
10671067
assert.Nil(t, dpopCtx)
10681068
})
10691069
}
1070+
1071+
// TestCheckTokenWithDPoP_LoggingPaths tests logging branches for better coverage
1072+
func TestCheckTokenWithDPoP_LoggingPaths(t *testing.T) {
1073+
t.Run("successful validation with debug logging", func(t *testing.T) {
1074+
logger := &mockLogger{}
1075+
validator := &mockTokenValidator{
1076+
validateFunc: func(ctx context.Context, token string) (any, error) {
1077+
return &mockTokenClaims{
1078+
hasConfirmation: true,
1079+
jkt: "test-jkt",
1080+
}, nil
1081+
},
1082+
dpopValidateFunc: func(ctx context.Context, proof string) (DPoPProofClaims, error) {
1083+
return &mockDPoPProofClaims{
1084+
publicKeyThumbprint: "test-jkt",
1085+
htm: "POST",
1086+
htu: "https://example.com/api",
1087+
iat: time.Now().Unix(),
1088+
}, nil
1089+
},
1090+
}
1091+
1092+
c, err := New(
1093+
WithValidator(validator),
1094+
WithLogger(logger),
1095+
WithDPoPMode(DPoPAllowed),
1096+
)
1097+
require.NoError(t, err)
1098+
1099+
claims, dpopCtx, err := c.CheckTokenWithDPoP(
1100+
context.Background(),
1101+
"token",
1102+
"proof",
1103+
"POST",
1104+
"https://example.com/api",
1105+
)
1106+
1107+
assert.NoError(t, err)
1108+
assert.NotNil(t, claims)
1109+
assert.NotNil(t, dpopCtx)
1110+
1111+
// Verify debug logs for successful validation
1112+
assert.NotEmpty(t, logger.debugCalls)
1113+
foundTokenLog := false
1114+
foundProofLog := false
1115+
for _, call := range logger.debugCalls {
1116+
if call.msg == "Access token validated successfully" {
1117+
foundTokenLog = true
1118+
}
1119+
if call.msg == "DPoP proof validated successfully" {
1120+
foundProofLog = true
1121+
}
1122+
}
1123+
assert.True(t, foundTokenLog, "Expected debug log for token validation")
1124+
assert.True(t, foundProofLog, "Expected debug log for DPoP proof validation")
1125+
})
1126+
1127+
t.Run("DPoP disabled with warning logging", func(t *testing.T) {
1128+
logger := &mockLogger{}
1129+
validator := &mockTokenValidator{
1130+
validateFunc: func(ctx context.Context, token string) (any, error) {
1131+
return &mockTokenClaims{
1132+
hasConfirmation: false,
1133+
}, nil
1134+
},
1135+
}
1136+
1137+
c, err := New(
1138+
WithValidator(validator),
1139+
WithLogger(logger),
1140+
WithDPoPMode(DPoPDisabled),
1141+
)
1142+
require.NoError(t, err)
1143+
1144+
claims, dpopCtx, err := c.CheckTokenWithDPoP(
1145+
context.Background(),
1146+
"token",
1147+
"proof-present-but-disabled", // DPoP proof present
1148+
"POST",
1149+
"https://example.com/api",
1150+
)
1151+
1152+
assert.NoError(t, err)
1153+
assert.NotNil(t, claims)
1154+
assert.Nil(t, dpopCtx)
1155+
1156+
// Verify warning log
1157+
assert.NotEmpty(t, logger.warnCalls)
1158+
found := false
1159+
for _, call := range logger.warnCalls {
1160+
if call.msg == "DPoP header present but DPoP is disabled, treating as Bearer token" {
1161+
found = true
1162+
break
1163+
}
1164+
}
1165+
assert.True(t, found, "Expected warning log for DPoP disabled")
1166+
})
1167+
1168+
t.Run("JKT mismatch with error logging", func(t *testing.T) {
1169+
logger := &mockLogger{}
1170+
validator := &mockTokenValidator{
1171+
validateFunc: func(ctx context.Context, token string) (any, error) {
1172+
return &mockTokenClaims{
1173+
hasConfirmation: true,
1174+
jkt: "expected-jkt",
1175+
}, nil
1176+
},
1177+
dpopValidateFunc: func(ctx context.Context, proof string) (DPoPProofClaims, error) {
1178+
return &mockDPoPProofClaims{
1179+
publicKeyThumbprint: "different-jkt",
1180+
htm: "POST",
1181+
htu: "https://example.com/api",
1182+
iat: time.Now().Unix(),
1183+
}, nil
1184+
},
1185+
}
1186+
1187+
c, err := New(
1188+
WithValidator(validator),
1189+
WithLogger(logger),
1190+
WithDPoPMode(DPoPAllowed),
1191+
)
1192+
require.NoError(t, err)
1193+
1194+
claims, dpopCtx, err := c.CheckTokenWithDPoP(
1195+
context.Background(),
1196+
"token",
1197+
"proof",
1198+
"POST",
1199+
"https://example.com/api",
1200+
)
1201+
1202+
assert.Error(t, err)
1203+
assert.Nil(t, claims)
1204+
assert.Nil(t, dpopCtx)
1205+
1206+
// Verify error log for JKT mismatch
1207+
assert.NotEmpty(t, logger.errorCalls)
1208+
found := false
1209+
for _, call := range logger.errorCalls {
1210+
if call.msg == "DPoP JKT mismatch" {
1211+
found = true
1212+
break
1213+
}
1214+
}
1215+
assert.True(t, found, "Expected error log for JKT mismatch")
1216+
})
1217+
1218+
t.Run("HTM mismatch with error logging", func(t *testing.T) {
1219+
logger := &mockLogger{}
1220+
validator := &mockTokenValidator{
1221+
validateFunc: func(ctx context.Context, token string) (any, error) {
1222+
return &mockTokenClaims{
1223+
hasConfirmation: true,
1224+
jkt: "test-jkt",
1225+
}, nil
1226+
},
1227+
dpopValidateFunc: func(ctx context.Context, proof string) (DPoPProofClaims, error) {
1228+
return &mockDPoPProofClaims{
1229+
publicKeyThumbprint: "test-jkt",
1230+
htm: "GET",
1231+
htu: "https://example.com/api",
1232+
iat: time.Now().Unix(),
1233+
}, nil
1234+
},
1235+
}
1236+
1237+
c, err := New(
1238+
WithValidator(validator),
1239+
WithLogger(logger),
1240+
WithDPoPMode(DPoPAllowed),
1241+
)
1242+
require.NoError(t, err)
1243+
1244+
claims, dpopCtx, err := c.CheckTokenWithDPoP(
1245+
context.Background(),
1246+
"token",
1247+
"proof",
1248+
"POST", // Different from proof HTM
1249+
"https://example.com/api",
1250+
)
1251+
1252+
assert.Error(t, err)
1253+
assert.Nil(t, claims)
1254+
assert.Nil(t, dpopCtx)
1255+
1256+
// Verify error log for HTM mismatch
1257+
assert.NotEmpty(t, logger.errorCalls)
1258+
found := false
1259+
for _, call := range logger.errorCalls {
1260+
if call.msg == "DPoP HTM mismatch" {
1261+
found = true
1262+
break
1263+
}
1264+
}
1265+
assert.True(t, found, "Expected error log for HTM mismatch")
1266+
})
1267+
1268+
t.Run("HTU mismatch with error logging", func(t *testing.T) {
1269+
logger := &mockLogger{}
1270+
validator := &mockTokenValidator{
1271+
validateFunc: func(ctx context.Context, token string) (any, error) {
1272+
return &mockTokenClaims{
1273+
hasConfirmation: true,
1274+
jkt: "test-jkt",
1275+
}, nil
1276+
},
1277+
dpopValidateFunc: func(ctx context.Context, proof string) (DPoPProofClaims, error) {
1278+
return &mockDPoPProofClaims{
1279+
publicKeyThumbprint: "test-jkt",
1280+
htm: "POST",
1281+
htu: "https://example.com/wrong-url",
1282+
iat: time.Now().Unix(),
1283+
}, nil
1284+
},
1285+
}
1286+
1287+
c, err := New(
1288+
WithValidator(validator),
1289+
WithLogger(logger),
1290+
WithDPoPMode(DPoPAllowed),
1291+
)
1292+
require.NoError(t, err)
1293+
1294+
claims, dpopCtx, err := c.CheckTokenWithDPoP(
1295+
context.Background(),
1296+
"token",
1297+
"proof",
1298+
"POST",
1299+
"https://example.com/api", // Different from proof HTU
1300+
)
1301+
1302+
assert.Error(t, err)
1303+
assert.Nil(t, claims)
1304+
assert.Nil(t, dpopCtx)
1305+
1306+
// Verify error log for HTU mismatch
1307+
assert.NotEmpty(t, logger.errorCalls)
1308+
found := false
1309+
for _, call := range logger.errorCalls {
1310+
if call.msg == "DPoP HTU mismatch" {
1311+
found = true
1312+
break
1313+
}
1314+
}
1315+
assert.True(t, found, "Expected error log for HTU mismatch")
1316+
})
1317+
1318+
t.Run("DPoP proof validation failure with error logging", func(t *testing.T) {
1319+
logger := &mockLogger{}
1320+
validator := &mockTokenValidator{
1321+
validateFunc: func(ctx context.Context, token string) (any, error) {
1322+
return &mockTokenClaims{
1323+
hasConfirmation: true,
1324+
jkt: "test-jkt",
1325+
}, nil
1326+
},
1327+
dpopValidateFunc: func(ctx context.Context, proof string) (DPoPProofClaims, error) {
1328+
return nil, errors.New("proof validation failed")
1329+
},
1330+
}
1331+
1332+
c, err := New(
1333+
WithValidator(validator),
1334+
WithLogger(logger),
1335+
WithDPoPMode(DPoPAllowed),
1336+
)
1337+
require.NoError(t, err)
1338+
1339+
claims, dpopCtx, err := c.CheckTokenWithDPoP(
1340+
context.Background(),
1341+
"token",
1342+
"invalid-proof",
1343+
"POST",
1344+
"https://example.com/api",
1345+
)
1346+
1347+
assert.Error(t, err)
1348+
assert.Nil(t, claims)
1349+
assert.Nil(t, dpopCtx)
1350+
1351+
// Verify error log for proof validation
1352+
assert.NotEmpty(t, logger.errorCalls)
1353+
found := false
1354+
for _, call := range logger.errorCalls {
1355+
if call.msg == "DPoP proof validation failed" {
1356+
found = true
1357+
break
1358+
}
1359+
}
1360+
assert.True(t, found, "Expected error log for proof validation failure")
1361+
})
1362+
}

0 commit comments

Comments
 (0)