auth

Paddy 2014-08-13 Parent:0aa843a306cd Child:1f04b1146cad

6:3423c552e249 Go to Latest

auth/authorize.go

Update package name. We've renamed the package to auth, so we may as well update the package name in all our files.

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) {
142 if r.Method == "GET" {
143 ctx.RenderConfirmation(w)
144 return
145 } else if r.Method != "POST" {
146 ctx.RenderError(w, InvalidMethodError)
147 return
148 }
150 if err := validateSession(r, ctx); err == ErrorNotAuthenticated {
151 ctx.RenderLogin(w)
152 return
153 } else if err != nil {
154 ctx.RenderError(w, err)
155 return
156 }
158 if r.FormValue("approved") != "true" {
159 redir, err := req.GetErrorRedirect(ErrorAccessDenied, "Request was not authorized.", ctx.Config.DocumentationDomain)
160 if err != nil {
161 ctx.RenderError(w, URIFormatError(req.RedirectURI))
162 return
163 }
164 http.Redirect(w, r, redir, http.StatusFound)
165 return
166 }
168 data := AuthorizeData{AuthRequest: req}
170 data.ExpiresIn = ctx.Config.AuthorizationExpiration
171 data.Code = newToken()
172 data.CreatedAt = time.Now()
174 err := ctx.Tokens.SaveAuthorization(data)
175 if err != nil {
176 redir, err := req.GetErrorRedirect(ErrorServerError, "Internal server error.", ctx.Config.DocumentationDomain)
177 if err != nil {
178 ctx.RenderError(w, URIFormatError(req.RedirectURI))
179 return
180 }
181 http.Redirect(w, r, redir, http.StatusFound)
182 return
183 }
185 redir, err := data.GetRedirect()
186 if err != nil {
187 ctx.RenderError(w, URIFormatError(req.RedirectURI))
188 return
189 }
190 http.Redirect(w, r, redir, http.StatusFound)
191 }
193 func (req AuthRequest) handleTokenRequest(w http.ResponseWriter, r *http.Request, ctx Context) {
195 if r.Method == "GET" {
196 ctx.RenderConfirmation(w)
197 return
198 } else if r.Method != "POST" {
199 ctx.RenderError(w, InvalidMethodError)
200 return
201 }
203 if err := validateSession(r, ctx); err == ErrorNotAuthenticated {
204 ctx.RenderLogin(w)
205 return
206 } else if err != nil {
207 ctx.RenderError(w, err)
208 return
209 }
211 if r.FormValue("approved") != "true" {
212 redir, err := req.GetErrorRedirect(ErrorAccessDenied, "Request was not authorized.", ctx.Config.DocumentationDomain)
213 if err != nil {
214 ctx.RenderError(w, URIFormatError(req.RedirectURI))
215 return
216 }
217 http.Redirect(w, r, redir, http.StatusFound)
218 return
219 }
221 data := AccessData{AuthRequest: req}
223 err := fillTokens(&data, false, ctx)
224 if err != nil {
225 ctx.RenderError(w, InternalServerError)
226 return
227 }
229 redir, err := data.GetRedirect(true)
230 if err != nil {
231 ctx.RenderError(w, URIFormatError(req.RedirectURI))
232 return
233 }
234 http.Redirect(w, r, redir, http.StatusFound)
235 }
237 func (data AuthorizeData) GetRedirect() (string, error) {
238 u, err := url.Parse(data.RedirectURI)
239 if err != nil {
240 return "", err
241 }
243 // add parameters
244 q := u.Query()
245 q.Set("code", data.Code)
246 q.Set("state", data.State)
247 u.RawQuery = q.Encode()
249 return u.String(), nil
250 }
252 func (req AuthRequest) GetErrorRedirect(code, description, uriBase string) (string, error) {
253 u, err := url.Parse(req.RedirectURI)
254 if err != nil {
255 return "", err
256 }
258 // add parameters
259 q := u.Query()
260 q.Set("error", code)
261 q.Set("error_description", description)
262 q.Set("error_uri", strings.Join([]string{
263 strings.TrimRight(uriBase, "/"),
264 strings.TrimLeft(code, "/"),
265 }, "/"))
266 q.Set("state", req.State)
267 u.RawQuery = q.Encode()
269 return u.String(), nil
270 }