auth

Paddy 2014-09-01 Parent:1aa3a85ff853

27:043906283c65 Go to Latest

auth/authorize.go.old

Rough out profiles. Create a Profile type that stores information about user profiles. Create a Login type that stores information about a login strategy for user profiles. This is necessary so that a user can login with their username or email address, with usernames not being required if an email address is supplied and email addresses not being required if a username is supplied.

History
1 package auth
3 import (
4 "net/http"
5 "net/url"
6 "time"
8 "strings"
9 "secondbit.org/uuid"
10 )
12 // AuthorizeRequestType is the type for OAuth param `response_type`
13 type AuthorizeRequestType string
15 const (
16 CodeAuthRT AuthorizeRequestType = "code"
17 TokenAuthRT = "token"
18 )
20 type AuthRequest struct {
21 Client Client
22 Scope string
23 RedirectURI string
24 State string
25 }
27 // Authorization data
28 type AuthorizeData struct {
29 // Authorization code
30 Code string
32 // Token expiration in seconds
33 ExpiresIn int32
35 // Date created
36 CreatedAt time.Time
38 ProfileID uuid.ID
40 AuthRequest
41 }
43 // IsExpired is true if authorization expired
44 func (d *AuthorizeData) IsExpired() bool {
45 return d.CreatedAt.Add(time.Duration(d.ExpiresIn) * time.Second).Before(time.Now())
46 }
48 // ExpireAt returns the expiration date
49 func (d *AuthorizeData) ExpireAt() time.Time {
50 return d.CreatedAt.Add(time.Duration(d.ExpiresIn) * time.Second)
51 }
53 // HandleAuthorizeRequest is the main http.HandlerFunc for handling
54 // authorization requests
55 func HandleAuthorizeRequest(w http.ResponseWriter, r *http.Request, ctx Context) {
56 r.ParseForm()
57 // create the authorization request
58 redirectURI := r.Form.Get("redirect_uri")
59 var err error
60 if redirectURI != "" {
61 redirectURI, err = url.QueryUnescape(redirectURI)
62 if err != nil {
63 ctx.RenderError(w, URIFormatError(redirectURI))
64 return
65 }
66 }
68 state := r.Form.Get("state")
69 scope := r.Form.Get("scope")
71 // must have a valid client
72 id, err := uuid.Parse(r.Form.Get("client_id"))
73 if err != nil {
74 ctx.RenderError(w, InvalidClientIDError(r.Form.Get("client_id")))
75 return
76 }
77 client, err := GetClient(id, ctx)
78 if err != nil {
79 if err == ClientNotFoundError {
80 ctx.RenderError(w, ClientNotFoundError)
81 return
82 }
83 if redirectURI == "" {
84 ctx.RenderError(w, URIMissingError)
85 return
86 }
87 req := AuthRequest{
88 RedirectURI: redirectURI,
89 Scope: scope,
90 State: state,
91 }
92 redir, err := req.GetErrorRedirect(ErrorServerError, "Internal server error.", ctx.Config.DocumentationDomain)
93 if err != nil {
94 ctx.RenderError(w, URIFormatError(redirectURI))
95 return
96 }
97 http.Redirect(w, r, redir, http.StatusFound)
98 return
99 }
100 if client.RedirectURI == "" {
101 ctx.RenderError(w, URIMissingError)
102 return
103 }
105 // check redirect uri
106 if redirectURI == "" {
107 redirectURI = client.RedirectURI
108 }
109 if err = validateURI(client.RedirectURI, redirectURI); err != nil {
110 ctx.RenderError(w, NewURIMismatchError(client.RedirectURI, redirectURI))
111 return
112 }
114 req := AuthRequest{
115 Client: client,
116 RedirectURI: redirectURI,
117 Scope: scope,
118 State: state,
119 }
121 requestType := AuthorizeRequestType(r.Form.Get("response_type"))
122 if ctx.Config.AllowedAuthorizeTypes.Exists(requestType) {
123 switch requestType {
124 case CodeAuthRT:
125 req.handleCodeRequest(w, r, ctx)
126 return
127 case TokenAuthRT:
128 req.handleTokenRequest(w, r, ctx)
129 return
130 }
131 }
132 redir, err := req.GetErrorRedirect(ErrorInvalidRequest, "Invalid response type.", ctx.Config.DocumentationDomain)
133 if err != nil {
134 ctx.RenderError(w, URIFormatError(req.RedirectURI))
135 return
136 }
137 http.Redirect(w, r, redir, http.StatusFound)
138 }
140 func (req AuthRequest) handleCodeRequest(w http.ResponseWriter, r *http.Request, ctx Context) {
141 if r.Method != "GET" && r.Method != "POST" {
142 ctx.RenderError(w, InvalidMethodError)
143 return
144 }
146 if err := validateSession(r, ctx); err == ErrorNotAuthenticated {
147 http.Redirect(w, r, "/auth/login?redirect_to="+url.QueryEscape(r.URL.String()), http.StatusFound)
148 return
149 } else if err != nil {
150 ctx.RenderError(w, err)
151 return
152 }
154 if r.Method == "GET" {
155 ctx.RenderConfirmation(w, r, req)
156 return
157 }
159 if r.FormValue("approved") != "true" {
160 redir, err := req.GetErrorRedirect(ErrorAccessDenied, "Request was not authorized.", ctx.Config.DocumentationDomain)
161 if err != nil {
162 ctx.RenderError(w, URIFormatError(req.RedirectURI))
163 return
164 }
165 http.Redirect(w, r, redir, http.StatusFound)
166 return
167 }
169 data := AuthorizeData{AuthRequest: req}
171 data.ExpiresIn = ctx.Config.AuthorizationExpiration
172 data.Code = newToken()
173 data.CreatedAt = time.Now()
175 err := ctx.Tokens.SaveAuthorization(data)
176 if err != nil {
177 redir, err := req.GetErrorRedirect(ErrorServerError, "Internal server error.", ctx.Config.DocumentationDomain)
178 if err != nil {
179 ctx.RenderError(w, URIFormatError(req.RedirectURI))
180 return
181 }
182 http.Redirect(w, r, redir, http.StatusFound)
183 return
184 }
186 redir, err := data.GetRedirect()
187 if err != nil {
188 ctx.RenderError(w, URIFormatError(req.RedirectURI))
189 return
190 }
191 http.Redirect(w, r, redir, http.StatusFound)
192 }
194 func (req AuthRequest) handleTokenRequest(w http.ResponseWriter, r *http.Request, ctx Context) {
195 if r.Method != "GET" && r.Method != "POST" {
196 ctx.RenderError(w, InvalidMethodError)
197 return
198 }
200 if err := validateSession(r, ctx); err == ErrorNotAuthenticated {
201 http.Redirect(w, r, "/auth/login?redirect_to="+url.QueryEscape(r.URL.String()), http.StatusFound)
202 return
203 } else if err != nil {
204 ctx.RenderError(w, err)
205 return
206 }
208 if r.Method == "GET" {
209 ctx.RenderConfirmation(w, r, req)
210 return
211 }
213 if r.FormValue("approved") != "true" {
214 redir, err := req.GetErrorRedirect(ErrorAccessDenied, "Request was not authorized.", ctx.Config.DocumentationDomain)
215 if err != nil {
216 ctx.RenderError(w, URIFormatError(req.RedirectURI))
217 return
218 }
219 http.Redirect(w, r, redir, http.StatusFound)
220 return
221 }
223 data := AccessData{AuthRequest: req}
225 err := fillTokens(&data, false, ctx)
226 if err != nil {
227 ctx.RenderError(w, InternalServerError)
228 return
229 }
231 redir, err := data.GetRedirect(true)
232 if err != nil {
233 ctx.RenderError(w, URIFormatError(req.RedirectURI))
234 return
235 }
236 http.Redirect(w, r, redir, http.StatusFound)
237 }
239 func (data AuthorizeData) GetRedirect() (string, error) {
240 u, err := url.Parse(data.RedirectURI)
241 if err != nil {
242 return "", err
243 }
245 // add parameters
246 q := u.Query()
247 q.Set("code", data.Code)
248 q.Set("state", data.State)
249 u.RawQuery = q.Encode()
251 return u.String(), nil
252 }
254 func (req AuthRequest) GetErrorRedirect(code, description, uriBase string) (string, error) {
255 u, err := url.Parse(req.RedirectURI)
256 if err != nil {
257 return "", err
258 }
260 // add parameters
261 q := u.Query()
262 q.Set("error", code)
263 q.Set("error_description", description)
264 q.Set("error_uri", strings.Join([]string{
265 strings.TrimRight(uriBase, "/"),
266 strings.TrimLeft(code, "/"),
267 }, "/"))
268 q.Set("state", req.State)
269 u.RawQuery = q.Encode()
271 return u.String(), nil
272 }