generated from TBD54566975/tbd-project-template
/
oauth2_auth.go
161 lines (139 loc) · 5.72 KB
/
oauth2_auth.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
package authorizationserver
import (
"fmt"
"github.com/TBD54566975/ssi-sdk/oidc/issuance"
"github.com/gin-gonic/gin"
"github.com/goccy/go-json"
"github.com/ory/fosite"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/tbd54566975/ssi-service/pkg/authorizationserver/request"
)
type AuthService struct {
issuerMetadata *issuance.IssuerMetadata
provider fosite.OAuth2Provider
}
func NewAuthService(issuerMetadata *issuance.IssuerMetadata, provider fosite.OAuth2Provider) *AuthService {
return &AuthService{issuerMetadata: issuerMetadata, provider: provider}
}
// AuthEndpoint is a Handler that implements https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#name-authorization-endpoint
func (s AuthService) AuthEndpoint(c *gin.Context) {
ar, err := s.provider.NewAuthorizeRequest(c, c.Request)
if err != nil {
logrus.WithError(err).Error("failed NewAuthorizeRequest")
s.provider.WriteAuthorizeError(c, c.Writer, ar, err)
return
}
authorizationDetailsJSON := ar.GetRequestForm().Get("authorization_details")
var authorizationDetails request.AuthorizationDetails
if err = json.Unmarshal([]byte(authorizationDetailsJSON), &authorizationDetails); err != nil {
logrus.WithError(err).Error("failed Unmarshal")
s.provider.WriteAuthorizeError(c, c.Writer, ar, err)
return
}
if err = authorizationDetails.IsValid(); err != nil {
logrus.WithError(err).Error("failed Unmarshal")
s.provider.WriteAuthorizeError(c, c.Writer, ar, err)
return
}
// If the Credential Issuer metadata contains an authorization_server parameter, the authorization detail's
// locations common data field MUST be set to the Credential Issuer Identifier value
if s.issuerMetadata.AuthorizationServer != nil {
for i, d := range authorizationDetails {
switch d.Type {
case "openid_credential":
if err := s.processOpenIDCredential(d); err != nil {
logrus.WithError(err).Error("failed processing openid_credential")
s.provider.WriteAuthorizeError(c, c.Writer, ar, err)
return
}
// TODO(https://github.com/TBD54566975/ssi-service/issues/368): support dynamic auth request
default:
err := errors.Errorf("the value of authorization_details[%d].type found was %q, which is not recognized", i, d.Type)
logrus.WithError(err).Error("unrecognized type")
s.provider.WriteAuthorizeError(c, c.Writer, ar, err)
return
}
}
}
// You have now access to authorizeRequest, Code ResponseTypes, Scopes ...
var requestedScopes string
for _, this := range ar.GetRequestedScopes() {
requestedScopes += fmt.Sprintf(`<li><input type="checkbox" name="scopes" value="%s">%s</li>`, this, this)
}
// Normally, this would be the place where you would check if the user is logged in and gives his consent.
// We're simplifying things and just checking if the request includes a valid username and password
if err = c.Request.ParseForm(); err != nil {
logrus.WithError(err).Error("failed parsing request form")
s.provider.WriteAuthorizeError(c, c.Writer, ar, err)
return
}
if c.Request.PostForm.Get("username") != "peter" {
c.Writer.Header().Set("Content-Type", "text/html; charset=utf-8")
_, _ = c.Writer.Write([]byte(`<h1>Login page</h1>`))
_, _ = c.Writer.Write([]byte(fmt.Sprintf(`
<p>Howdy! This is the log in page. For this example, it is enough to supply the username.</p>
<form method="post">
<p>
By logging in, you consent to grant these scopes:
<ul>%s</ul>
</p>
<input type="text" name="username" /> <small>try peter</small><br>
<input type="submit">
</form>
`, requestedScopes)))
return
}
// let's see what scopes the user gave consent to
for _, scope := range c.Request.PostForm["scopes"] {
ar.GrantScope(scope)
}
// Now that the user is authorized, we set up a session:
mySessionData := newSession("peter")
// When using the HMACSHA strategy you must use something that implements the HMACSessionContainer.
// It brings you the power of overriding the default values.
//
// mySessionData.HMACSession = &strategy.HMACSession{
// AccessTokenExpiry: time.Now().Add(time.Day),
// AuthorizeCodeExpiry: time.Now().Add(time.Day),
// }
//
// If you're using the JWT strategy, there's currently no distinction between access token and authorize code claims.
// Therefore, you both access token and authorize code will have the same "exp" claim. If this is something you
// need let us know on github.
//
// mySessionData.JWTClaims.ExpiresAt = time.Now().Add(time.Day)
// It's also wise to check the requested scopes, e.g.:
// if ar.GetRequestedScopes().Has("admin") {
// http.Error(rw, "you're not allowed to do that", http.StatusForbidden)
// return
// }
// Now we need to get a response. This is the place where the AuthorizeEndpointHandlers kick in and start processing the request.
// NewAuthorizeResponse is capable of running multiple response type handlers which in turn enables this library
// to support open id connect.
response, err := s.provider.NewAuthorizeResponse(c, ar, mySessionData)
// Catch any errors, e.g.:
// * unknown client
// * invalid redirect
// * ...
if err != nil {
logrus.WithError(err).Error("failed NewAuthorizeResponse")
s.provider.WriteAuthorizeError(c, c.Writer, ar, err)
return
}
// Last but not least, send the response!
s.provider.WriteAuthorizeResponse(c, c.Writer, ar, response)
}
func (s AuthService) processOpenIDCredential(d request.AuthorizationDetail) error {
if len(d.Locations) != 1 {
return errors.New("locations expected to have a single element")
}
if d.Locations[0] != s.issuerMetadata.CredentialIssuer.String() {
return errors.Errorf(
"locations[0] expected to be equal to %q, but received %q",
s.issuerMetadata.CredentialIssuer.String(),
d.Locations[0],
)
}
return nil
}