auth
2014-07-18
Child:7b9e0fc20256
auth/authorize.go
Start rewriting the repo. This code originally was a carbon copy of https://github.com/RangelReale/osin, but I am methodically stripping out the extensible nature of it for a simpler interface, while simultaneously bringing the style into line with the Ducky style.
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/authorize.go Fri Jul 18 07:13:22 2014 -0400 1.3 @@ -0,0 +1,219 @@ 1.4 +package oauth2 1.5 + 1.6 +import ( 1.7 + "net/http" 1.8 + "net/url" 1.9 + "time" 1.10 + 1.11 + "secondbit.org/uuid" 1.12 +) 1.13 + 1.14 +// AuthorizeRequestType is the type for OAuth param `response_type` 1.15 +type AuthorizeRequestType string 1.16 + 1.17 +const ( 1.18 + CodeAuthRT AuthorizeRequestType = "code" 1.19 + TokenAuthRT = "token" 1.20 +) 1.21 + 1.22 +// Authorize request information 1.23 +type AuthorizeRequest struct { 1.24 + Type AuthorizeRequestType 1.25 + Client Client 1.26 + Scope string 1.27 + RedirectURI string 1.28 + State string 1.29 + 1.30 + // Token expiration in seconds. Change if different from default. 1.31 + // If type = TokenAuthRT, this expiration will be for the ACCESS token. 1.32 + Expiration int32 1.33 +} 1.34 + 1.35 +// Authorization data 1.36 +type AuthorizeData struct { 1.37 + // Client information 1.38 + Client Client 1.39 + 1.40 + // Authorization code 1.41 + Code string 1.42 + 1.43 + // Token expiration in seconds 1.44 + ExpiresIn int32 1.45 + 1.46 + // Requested scope 1.47 + Scope string 1.48 + 1.49 + // Redirect URI from request 1.50 + RedirectURI string 1.51 + 1.52 + // State data from request 1.53 + State string 1.54 + 1.55 + // Date created 1.56 + CreatedAt time.Time 1.57 +} 1.58 + 1.59 +// IsExpired is true if authorization expired 1.60 +func (d *AuthorizeData) IsExpired() bool { 1.61 + return d.CreatedAt.Add(time.Duration(d.ExpiresIn) * time.Second).Before(time.Now()) 1.62 +} 1.63 + 1.64 +// ExpireAt returns the expiration date 1.65 +func (d *AuthorizeData) ExpireAt() time.Time { 1.66 + return d.CreatedAt.Add(time.Duration(d.ExpiresIn) * time.Second) 1.67 +} 1.68 + 1.69 +// HandleAuthorizeRequest is the main http.HandlerFunc for handling 1.70 +// authorization requests 1.71 +func HandleAuthorizeRequest(w http.ResponseWriter, r *http.Request, ctx Context) { 1.72 + r.ParseForm() 1.73 + 1.74 + requestType := AuthorizeRequestType(r.Form.Get("response_type")) 1.75 + if ctx.Config.AllowedAuthorizeTypes.Exists(requestType) { 1.76 + switch requestType { 1.77 + case CodeAuthRT: 1.78 + handleCodeRequest(w, r, ctx) 1.79 + return 1.80 + case TokenAuthRT: 1.81 + handleTokenRequest(w, r, ctx) 1.82 + return 1.83 + } 1.84 + } 1.85 + // TODO: return error 1.86 +} 1.87 + 1.88 +func handleCodeRequest(w http.ResponseWriter, r *http.Request, ctx Context) { 1.89 + // create the authorization request 1.90 + unescapedURI, err := url.QueryUnescape(r.Form.Get("redirect_uri")) 1.91 + if err != nil { 1.92 + unescapedURI = "" 1.93 + } 1.94 + ret := &AuthorizeRequest{ 1.95 + Type: CodeAuthRT, 1.96 + State: r.Form.Get("state"), 1.97 + Scope: r.Form.Get("scope"), 1.98 + RedirectURI: unescapedURI, 1.99 + Expiration: ctx.Config.AuthorizationExpiration, 1.100 + } 1.101 + 1.102 + // must have a valid client 1.103 + id, err := uuid.Parse(r.Form.Get("client_id")) 1.104 + if err != nil { 1.105 + // TODO: return error 1.106 + return 1.107 + } 1.108 + ret.Client, err = GetClient(id, ctx) 1.109 + if err != nil { 1.110 + // TODO: return error 1.111 + return 1.112 + } 1.113 + if ret.Client.RedirectURI == "" { 1.114 + // TODO: return error 1.115 + return 1.116 + } 1.117 + 1.118 + // check redirect uri 1.119 + if ret.RedirectURI == "" { 1.120 + ret.RedirectURI = ret.Client.RedirectURI 1.121 + } 1.122 + if err = ValidateURI(ret.Client.RedirectURI, ret.RedirectURI); err != nil { 1.123 + // TODO: return error 1.124 + return 1.125 + } 1.126 + 1.127 + // TODO: do redirect with ret data 1.128 +} 1.129 + 1.130 +func handleTokenRequest(w http.ResponseWriter, r *http.Request, ctx Context) { 1.131 + // create the authorization request 1.132 + unescapedURI, err := url.QueryUnescape(r.Form.Get("redirect_uri")) 1.133 + if err != nil { 1.134 + unescapedURI = "" 1.135 + } 1.136 + ret := &AuthorizeRequest{ 1.137 + Type: TokenAuthRT, 1.138 + State: r.Form.Get("state"), 1.139 + Scope: r.Form.Get("scope"), 1.140 + RedirectURI: unescapedURI, 1.141 + // this type will generate a token directly, use access token expiration instead. 1.142 + Expiration: ctx.Config.AccessExpiration, 1.143 + } 1.144 + 1.145 + // must have a valid client 1.146 + id, err := uuid.Parse(r.Form.Get("client_id")) 1.147 + if err != nil { 1.148 + // TODO: return error 1.149 + return 1.150 + } 1.151 + ret.Client, err = GetClient(id, ctx) 1.152 + if err != nil { 1.153 + // TODO: return error 1.154 + return 1.155 + } 1.156 + if ret.Client.RedirectURI == "" { 1.157 + // TODO: return error 1.158 + return 1.159 + } 1.160 + 1.161 + // check redirect uri 1.162 + if ret.RedirectURI == "" { 1.163 + ret.RedirectURI = ret.Client.RedirectURI 1.164 + } 1.165 + if err = ValidateURI(ret.Client.RedirectURI, ret.RedirectURI); err != nil { 1.166 + // TODO: return error 1.167 + } 1.168 + 1.169 + // TODO: redirect with ret information 1.170 +} 1.171 + 1.172 +func FinishAuthorizeRequest(w http.ResponseWriter, r *http.Request, ar *AuthorizeRequest, ctx Context) { 1.173 + // TODO: check if authorized? 1.174 + if ar.Type == TokenAuthRT { 1.175 + // TODO: w.SetRedirectFragment(true) was called... 1.176 + 1.177 + // generate token directly 1.178 + ret := AccessRequest{ 1.179 + Code: "", 1.180 + Client: ar.Client, 1.181 + RedirectURI: ar.RedirectURI, 1.182 + Scope: ar.Scope, 1.183 + GenerateRefresh: false, // per the RFC, should NOT generate a refresh token in this case 1.184 + Expiration: ar.Expiration, 1.185 + } 1.186 + // TODO: ret.type was implicit 1.187 + // TODO: ret.Authorized was true 1.188 + FinishAccessRequest(w, r, ret, ctx) 1.189 + } else { 1.190 + // generate authorization token 1.191 + ret := AuthorizeData{ 1.192 + Client: ar.Client, 1.193 + CreatedAt: time.Now(), 1.194 + ExpiresIn: ar.Expiration, 1.195 + RedirectURI: ar.RedirectURI, 1.196 + State: ar.State, 1.197 + Scope: ar.Scope, 1.198 + Code: newToken(), 1.199 + } 1.200 + 1.201 + // save authorization token 1.202 + err := saveAuthorize(ret, ctx) 1.203 + if err != nil { 1.204 + // TODO: return error 1.205 + return 1.206 + } 1.207 + 1.208 + // TODO: redirect with ret.Code and ret.State 1.209 + } 1.210 +} 1.211 + 1.212 +func loadAuthorize(code string, ctx Context) (AuthorizeData, error) { 1.213 + return AuthorizeData{}, nil 1.214 +} 1.215 + 1.216 +func saveAuthorize(ret AuthorizeData, ctx Context) error { 1.217 + return nil 1.218 +} 1.219 + 1.220 +func removeAuthorize(code string, ctx Context) error { 1.221 + return nil 1.222 +}