18 "github.com/go-jose/go-jose/v3"
19 "github.com/golang-jwt/jwt/v5"
20 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
21 . "github.com/onsi/gomega"
22 "github.com/stretchr/testify/assert"
25type MyKeyData struct {
26 PubKey crypto.PublicKey
27 PrivKey *rsa.PrivateKey
28 PubJWK jose.JSONWebKey
31func newLoginGovServer(body []byte) (*url.URL, *httptest.Server) {
32 s := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
35 u, _ := url.Parse(s.URL)
39func newPrivateKeyBytes() ([]byte, error) {
40 privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
45 keyBytes, err := x509.MarshalPKCS8PrivateKey(privateKey)
50 privateKeyBlock := &pem.Block{
51 Type: "RSA PRIVATE KEY",
55 if err := pem.Encode(b, privateKeyBlock); err != nil {
62func newLoginGovProvider() (*LoginGovProvider, *MyKeyData, error) {
63 key, err := rsa.GenerateKey(rand.Reader, 2048)
67 serverKey := &MyKeyData{
70 PubJWK: jose.JSONWebKey{
73 Algorithm: string(jose.RS256),
78 privKey, err := newPrivateKeyBytes()
83 l, err := NewLoginGovProvider(
87 RedeemURL: &url.URL{},
88 ProfileURL: &url.URL{},
89 ValidateURL: &url.URL{},
91 options.LoginGovOptions{
92 JWTKey: string(privKey),
96 return l, serverKey, err
99func TestNewLoginGovProvider(t *testing.T) {
102 privKey, err := newPrivateKeyBytes()
103 g.Expect(err).ToNot(HaveOccurred())
105 // Test that defaults are set when calling for a new provider with nothing set
106 provider, err := NewLoginGovProvider(&ProviderData{}, options.LoginGovOptions{
107 JWTKey: string(privKey),
109 g.Expect(err).ToNot(HaveOccurred())
111 providerData := provider.Data()
112 g.Expect(providerData.ProviderName).To(Equal("login.gov"))
113 g.Expect(providerData.LoginURL.String()).To(Equal("https://secure.login.gov/openid_connect/authorize"))
114 g.Expect(providerData.RedeemURL.String()).To(Equal("https://secure.login.gov/api/openid_connect/token"))
115 g.Expect(providerData.ProfileURL.String()).To(Equal("https://secure.login.gov/api/openid_connect/userinfo"))
116 g.Expect(providerData.ValidateURL.String()).To(Equal("https://secure.login.gov/api/openid_connect/userinfo"))
117 g.Expect(providerData.Scope).To(Equal("email openid"))
120func TestLoginGovProviderOverrides(t *testing.T) {
121 privKey, err := newPrivateKeyBytes()
122 assert.NoError(t, err)
124 p, err := NewLoginGovProvider(
129 Path: "/oauth/auth"},
133 Path: "/oauth/token"},
134 ProfileURL: &url.URL{
137 Path: "/oauth/profile"},
139 options.LoginGovOptions{
140 JWTKey: string(privKey),
142 assert.NoError(t, err)
143 assert.NotEqual(t, nil, p)
144 assert.Equal(t, "login.gov", p.Data().ProviderName)
145 assert.Equal(t, "https://example.com/oauth/auth",
146 p.Data().LoginURL.String())
147 assert.Equal(t, "https://example.com/oauth/token",
148 p.Data().RedeemURL.String())
149 assert.Equal(t, "https://example.com/oauth/profile",
150 p.Data().ProfileURL.String())
151 assert.Equal(t, "profile", p.Data().Scope)
154func TestLoginGovProviderSessionData(t *testing.T) {
155 p, serverkey, err := newLoginGovProvider()
156 assert.NotEqual(t, nil, p)
157 assert.NoError(t, err)
159 // Set up the redeem endpoint here
160 type loginGovRedeemResponse struct {
161 AccessToken string `json:"access_token"`
162 TokenType string `json:"token_type"`
163 ExpiresIn int64 `json:"expires_in"`
164 IDToken string `json:"id_token"`
166 expiresIn := int64(60)
167 type MyCustomClaims struct {
168 Acr string `json:"acr"`
169 Nonce string `json:"nonce"`
170 Email string `json:"email"`
171 EmailVerified bool `json:"email_verified"`
172 GivenName string `json:"given_name"`
173 FamilyName string `json:"family_name"`
174 Birthdate string `json:"birthdate"`
175 AtHash string `json:"at_hash"`
176 CHash string `json:"c_hash"`
179 claims := MyCustomClaims{
180 "http://idmanagement.gov/ns/assurance/loa/1",
182 "timothy.spencer@gsa.gov",
189 jwt.RegisteredClaims{
190 Audience: jwt.ClaimStrings{"Audience"},
191 ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Duration(expiresIn) * time.Second)),
192 IssuedAt: jwt.NewNumericDate(time.Now()),
193 Issuer: "https://idp.int.login.gov",
194 NotBefore: jwt.NewNumericDate(time.Now().Add(-1 * time.Second)),
195 Subject: "b2d2d115-1d7e-4579-b9d6-f8e84f4f56ca",
198 idtoken := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
199 signedidtoken, err := idtoken.SignedString(serverkey.PrivKey)
200 assert.NoError(t, err)
201 body, err := json.Marshal(loginGovRedeemResponse{
202 AccessToken: "a1234",
204 ExpiresIn: expiresIn,
205 IDToken: signedidtoken,
207 assert.NoError(t, err)
208 var server *httptest.Server
209 p.RedeemURL, server = newLoginGovServer(body)
212 // Set up the user endpoint here
213 type loginGovUserResponse struct {
214 Email string `json:"email"`
215 EmailVerified bool `json:"email_verified"`
216 Subject string `json:"sub"`
218 userbody, err := json.Marshal(loginGovUserResponse{
219 Email: "timothy.spencer@gsa.gov",
221 Subject: "b2d2d115-1d7e-4579-b9d6-f8e84f4f56ca",
223 assert.NoError(t, err)
224 var userserver *httptest.Server
225 p.ProfileURL, userserver = newLoginGovServer(userbody)
226 defer userserver.Close()
228 // Set up the PubJWKURL endpoint here used to verify the JWT
229 var pubkeys jose.JSONWebKeySet
230 pubkeys.Keys = append(pubkeys.Keys, serverkey.PubJWK)
231 pubjwkbody, err := json.Marshal(pubkeys)
232 assert.NoError(t, err)
233 var pubjwkserver *httptest.Server
234 p.PubJWKURL, pubjwkserver = newLoginGovServer(pubjwkbody)
235 defer pubjwkserver.Close()
237 session, err := p.Redeem(context.Background(), "http://redirect/", "code1234", "123")
238 assert.NoError(t, err)
239 assert.NotEqual(t, session, nil)
240 assert.Equal(t, "timothy.spencer@gsa.gov", session.Email)
241 assert.Equal(t, "a1234", session.AccessToken)
243 // The test ought to run in under 2 seconds. If not, you may need to bump this up.
244 assert.InDelta(t, session.ExpiresOn.Unix(), time.Now().Unix()+expiresIn, 2)
247func TestLoginGovProviderBadNonce(t *testing.T) {
248 p, serverkey, err := newLoginGovProvider()
249 assert.NotEqual(t, nil, p)
250 assert.NoError(t, err)
252 // Set up the redeem endpoint here
253 type loginGovRedeemResponse struct {
254 AccessToken string `json:"access_token"`
255 TokenType string `json:"token_type"`
256 ExpiresIn int64 `json:"expires_in"`
257 IDToken string `json:"id_token"`
259 expiresIn := int64(60)
260 type MyCustomClaims struct {
261 Acr string `json:"acr"`
262 Nonce string `json:"nonce"`
263 Email string `json:"email"`
264 EmailVerified bool `json:"email_verified"`
265 GivenName string `json:"given_name"`
266 FamilyName string `json:"family_name"`
267 Birthdate string `json:"birthdate"`
268 AtHash string `json:"at_hash"`
269 CHash string `json:"c_hash"`
272 claims := MyCustomClaims{
273 "http://idmanagement.gov/ns/assurance/loa/1",
275 "timothy.spencer@gsa.gov",
282 jwt.RegisteredClaims{
283 Audience: jwt.ClaimStrings{"Audience"},
284 ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Duration(expiresIn) * time.Second)),
285 IssuedAt: jwt.NewNumericDate(time.Now()),
286 Issuer: "https://idp.int.login.gov",
287 NotBefore: jwt.NewNumericDate(time.Now().Add(-1 * time.Second)),
288 Subject: "b2d2d115-1d7e-4579-b9d6-f8e84f4f56ca",
291 idtoken := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
292 signedidtoken, err := idtoken.SignedString(serverkey.PrivKey)
293 assert.NoError(t, err)
294 body, err := json.Marshal(loginGovRedeemResponse{
295 AccessToken: "a1234",
297 ExpiresIn: expiresIn,
298 IDToken: signedidtoken,
300 assert.NoError(t, err)
301 var server *httptest.Server
302 p.RedeemURL, server = newLoginGovServer(body)
305 // Set up the user endpoint here
306 type loginGovUserResponse struct {
307 Email string `json:"email"`
308 EmailVerified bool `json:"email_verified"`
309 Subject string `json:"sub"`
311 userbody, err := json.Marshal(loginGovUserResponse{
312 Email: "timothy.spencer@gsa.gov",
314 Subject: "b2d2d115-1d7e-4579-b9d6-f8e84f4f56ca",
316 assert.NoError(t, err)
317 var userserver *httptest.Server
318 p.ProfileURL, userserver = newLoginGovServer(userbody)
319 defer userserver.Close()
321 // Set up the PubJWKURL endpoint here used to verify the JWT
322 var pubkeys jose.JSONWebKeySet
323 pubkeys.Keys = append(pubkeys.Keys, serverkey.PubJWK)
324 pubjwkbody, err := json.Marshal(pubkeys)
325 assert.NoError(t, err)
326 var pubjwkserver *httptest.Server
327 p.PubJWKURL, pubjwkserver = newLoginGovServer(pubjwkbody)
328 defer pubjwkserver.Close()
330 _, err = p.Redeem(context.Background(), "http://redirect/", "code1234", "123")
332 // The "badfakenonce" in the idtoken above should cause this to error out
336func TestLoginGovProviderGetLoginURL(t *testing.T) {
337 p, _, _ := newLoginGovProvider()
338 result := p.GetLoginURL("http://redirect/", "", "", url.Values{})
339 assert.Contains(t, result, "acr_values="+url.QueryEscape("http://idmanagement.gov/ns/assurance/loa/1"))
340 assert.Contains(t, result, "nonce=fakenonce")
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list