@@ -3,6 +3,7 @@ package apptest
3
3
import (
4
4
"bufio"
5
5
"context"
6
+ "crypto/rand"
6
7
"encoding/json"
7
8
"fmt"
8
9
"io"
@@ -463,7 +464,7 @@ func Run(t *testing.T, appHostIsPrimary bool, factory DeploymentFactory) {
463
464
appClient .SetSessionToken ("" )
464
465
465
466
// Try to load the application without authentication.
466
- u := c .appURL
467
+ u := * c .appURL
467
468
u .Path = path .Join (u .Path , "/test" )
468
469
req , err := http .NewRequestWithContext (ctx , http .MethodGet , u .String (), nil )
469
470
require .NoError (t , err )
@@ -500,7 +501,7 @@ func Run(t *testing.T, appHostIsPrimary bool, factory DeploymentFactory) {
500
501
501
502
// Copy the query parameters and then check equality.
502
503
u .RawQuery = gotLocation .RawQuery
503
- require .Equal (t , u , gotLocation )
504
+ require .Equal (t , u , * gotLocation )
504
505
505
506
// Verify the API key is set.
506
507
encryptedAPIKey := gotLocation .Query ().Get (workspaceapps .SubdomainProxyAPIKeyParam )
@@ -580,6 +581,70 @@ func Run(t *testing.T, appHostIsPrimary bool, factory DeploymentFactory) {
580
581
resp .Body .Close ()
581
582
require .Equal (t , http .StatusOK , resp .StatusCode )
582
583
})
584
+
585
+ t .Run ("BadJWT" , func (t * testing.T ) {
586
+ t .Parallel ()
587
+
588
+ ctx , cancel := context .WithTimeout (context .Background (), testutil .WaitLong )
589
+ defer cancel ()
590
+
591
+ currentKeyStr := appDetails .SDKClient .SessionToken ()
592
+ appClient := appDetails .AppClient (t )
593
+ appClient .SetSessionToken ("" )
594
+ u := * c .appURL
595
+ u .Path = path .Join (u .Path , "/test" )
596
+ req , err := http .NewRequestWithContext (ctx , http .MethodGet , u .String (), nil )
597
+ require .NoError (t , err )
598
+
599
+ var resp * http.Response
600
+ resp , err = doWithRetries (t , appClient , req )
601
+ require .NoError (t , err )
602
+
603
+ if ! assert .Equal (t , http .StatusSeeOther , resp .StatusCode ) {
604
+ dump , err := httputil .DumpResponse (resp , true )
605
+ require .NoError (t , err )
606
+ t .Log (string (dump ))
607
+ }
608
+ resp .Body .Close ()
609
+
610
+ // Check that the Location is correct.
611
+ gotLocation , err := resp .Location ()
612
+ require .NoError (t , err )
613
+ // This should always redirect to the primary access URL.
614
+ require .Equal (t , appDetails .SDKClient .URL .Host , gotLocation .Host )
615
+ require .Equal (t , "/api/v2/applications/auth-redirect" , gotLocation .Path )
616
+ require .Equal (t , u .String (), gotLocation .Query ().Get ("redirect_uri" ))
617
+
618
+ // Load the application auth-redirect endpoint.
619
+ resp , err = requestWithRetries (ctx , t , appDetails .SDKClient , http .MethodGet , "/api/v2/applications/auth-redirect" , nil , codersdk .WithQueryParam (
620
+ "redirect_uri" , u .String (),
621
+ ))
622
+ require .NoError (t , err )
623
+ defer resp .Body .Close ()
624
+
625
+ require .Equal (t , http .StatusSeeOther , resp .StatusCode )
626
+ gotLocation , err = resp .Location ()
627
+ require .NoError (t , err )
628
+
629
+ badToken := generateBadJWT (t , workspaceapps.EncryptedAPIKeyPayload {
630
+ APIKey : currentKeyStr ,
631
+ })
632
+
633
+ gotLocation .RawQuery = (url.Values {
634
+ workspaceapps .SubdomainProxyAPIKeyParam : {badToken },
635
+ }).Encode ()
636
+
637
+ req , err = http .NewRequestWithContext (ctx , "GET" , gotLocation .String (), nil )
638
+ require .NoError (t , err )
639
+ resp , err = appClient .HTTPClient .Do (req )
640
+ require .NoError (t , err )
641
+ defer resp .Body .Close ()
642
+ require .Equal (t , http .StatusBadRequest , resp .StatusCode )
643
+ body , err := io .ReadAll (resp .Body )
644
+ require .NoError (t , err )
645
+ require .Contains (t , string (body ), "Could not decrypt API key. Please remove the query parameter and try again." )
646
+
647
+ })
583
648
}
584
649
})
585
650
})
@@ -1789,3 +1854,24 @@ func assertWorkspaceLastUsedAtNotUpdated(t testing.TB, details *Details) {
1789
1854
require .NoError (t , err )
1790
1855
require .Equal (t , before .LastUsedAt , after .LastUsedAt , "workspace LastUsedAt updated when it should not have been" )
1791
1856
}
1857
+
1858
+ // generateBadJWT generates a JWT with a random key. It's intended to emulate the old-style JWT's we generated.
1859
+ func generateBadJWT (t * testing.T , claims interface {}) string {
1860
+ t .Helper ()
1861
+
1862
+ var buf [64 ]byte
1863
+ _ , err := rand .Read (buf [:])
1864
+ require .NoError (t , err )
1865
+ signer , err := jose .NewSigner (jose.SigningKey {
1866
+ Algorithm : jose .HS512 ,
1867
+ Key : buf [:],
1868
+ }, nil )
1869
+ require .NoError (t , err )
1870
+ payload , err := json .Marshal (claims )
1871
+ require .NoError (t , err )
1872
+ signed , err := signer .Sign (payload )
1873
+ require .NoError (t , err )
1874
+ compact , err := signed .CompactSerialize ()
1875
+ require .NoError (t , err )
1876
+ return compact
1877
+ }
0 commit comments