1# oidc
2[![Go Reference](https://pkg.go.dev/badge/github.com/hashicorp/cap/oidc.svg)](https://pkg.go.dev/github.com/hashicorp/cap/oidc)
3
4oidc is a package for writing clients that integrate with OIDC Providers using
5OIDC flows.
6
7Primary types provided by the package:
8
9* `Request`: represents one OIDC authentication flow for a user.  It contains the
10  data needed to uniquely represent that one-time flow across the multiple
11  interactions needed to complete the OIDC flow the user is attempting.  All
12  Requests contain an expiration for the user's OIDC flow.
13
14* `Token`: represents an OIDC id_token, as well as an Oauth2 access_token and
15  refresh_token (including the the access_token expiry)
16
17* `Config`: provides the configuration for a typical 3-legged OIDC
18  authorization code flow (for example: client ID/Secret, redirectURL, supported
19  signing algorithms, additional scopes requested, etc)
20
21* `Provider`: provides integration with an OIDC provider.
22  The provider provides capabilities like: generating an auth URL, exchanging
23  codes for tokens, verifying tokens, making user info requests, etc.
24
25* `Alg`: represents asymmetric signing algorithms
26
27* `Error`: provides an error and provides the ability to specify an error code,
28  operation that raised the error, the kind of error, and any wrapped error
29
30#### [oidc.callback](callback/)
31[![Go Reference](https://pkg.go.dev/badge/github.com/hashicorp/cap/oidc/callback.svg)](https://pkg.go.dev/github.com/hashicorp/cap/oidc/callback)
32
33The callback package includes handlers (http.HandlerFunc) which can be used
34for the callback leg an OIDC flow. Callback handlers for both the authorization
35code flow (with optional PKCE) and the implicit flow are provided.
36
37<hr>
38
39### Examples:
40
41* [CLI example](examples/cli/) which implements an OIDC
42  user authentication CLI.
43
44* [SPA example](examples/spa) which implements an OIDC user
45  authentication SPA (single page app).
46
47<hr>
48
49Example of a provider using an authorization code flow:
50
51```go
52// Create a new provider config
53pc, err := oidc.NewConfig(
54  "http://your-issuer.com/",
55  "your_client_id",
56  "your_client_secret",
57  []oidc.Alg{oidc.RS256},
58  []string{"http://your_redirect_url"},
59)
60if err != nil {
61  // handle error
62}
63
64// Create a provider
65p, err := oidc.NewProvider(pc)
66if err != nil {
67  // handle error
68}
69defer p.Done()
70
71
72// Create a Request for a user's authentication attempt that will use the
73// authorization code flow.  (See NewRequest(...) using the WithPKCE and
74// WithImplicit options for creating a Request that uses those flows.)
75oidcRequest, err := oidc.NewRequest(2 * time.Minute, "http://your_redirect_url/callback")
76if err != nil {
77  // handle error
78}
79
80// Create an auth URL
81authURL, err := p.AuthURL(context.Background(), oidcRequest)
82if err != nil {
83  // handle error
84}
85fmt.Println("open url to kick-off authentication: ", authURL)
86```
87
88Create a http.Handler for OIDC authentication response redirects.
89
90```go
91func NewHandler(ctx context.Context, p *oidc.Provider, rw callback.RequestReader) (http.HandlerFunc, error)
92  if p == nil {
93    // handle error
94  }
95  if rw == nil {
96    // handle error
97  }
98  return func(w http.ResponseWriter, r *http.Request) {
99    oidcRequest, err := rw.Read(ctx, req.FormValue("state"))
100    if err != nil {
101      // handle error
102    }
103    // Exchange(...) will verify the tokens before returning.
104    token, err := p.Exchange(ctx, oidcRequest, req.FormValue("state"), req.FormValue("code"))
105    if err != nil {
106      // handle error
107    }
108    var claims map[string]interface{}
109    if err := t.IDToken().Claims(&claims); err != nil {
110      // handle error
111    }
112
113    // Get the user's claims via the provider's UserInfo endpoint
114    var infoClaims map[string]interface{}
115    err = p.UserInfo(ctx, token.StaticTokenSource(), claims["sub"].(string), &infoClaims)
116    if err != nil {
117      // handle error
118    }
119    resp := struct {
120	  IDTokenClaims  map[string]interface{}
121	  UserInfoClaims map[string]interface{}
122    }{claims, infoClaims}
123    enc := json.NewEncoder(w)
124    if err := enc.Encode(resp); err != nil {
125	    // handle error
126    }
127  }
128}
129```
130
131
132