auth

Paddy 2014-07-18 Child:7b9e0fc20256

0:7a6f64db7246 Go to Latest

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.

History
paddy@0 1 package oauth2
paddy@0 2
paddy@0 3 import (
paddy@0 4 "net/http"
paddy@0 5 "net/url"
paddy@0 6 "time"
paddy@0 7
paddy@0 8 "secondbit.org/uuid"
paddy@0 9 )
paddy@0 10
paddy@0 11 // AuthorizeRequestType is the type for OAuth param `response_type`
paddy@0 12 type AuthorizeRequestType string
paddy@0 13
paddy@0 14 const (
paddy@0 15 CodeAuthRT AuthorizeRequestType = "code"
paddy@0 16 TokenAuthRT = "token"
paddy@0 17 )
paddy@0 18
paddy@0 19 // Authorize request information
paddy@0 20 type AuthorizeRequest struct {
paddy@0 21 Type AuthorizeRequestType
paddy@0 22 Client Client
paddy@0 23 Scope string
paddy@0 24 RedirectURI string
paddy@0 25 State string
paddy@0 26
paddy@0 27 // Token expiration in seconds. Change if different from default.
paddy@0 28 // If type = TokenAuthRT, this expiration will be for the ACCESS token.
paddy@0 29 Expiration int32
paddy@0 30 }
paddy@0 31
paddy@0 32 // Authorization data
paddy@0 33 type AuthorizeData struct {
paddy@0 34 // Client information
paddy@0 35 Client Client
paddy@0 36
paddy@0 37 // Authorization code
paddy@0 38 Code string
paddy@0 39
paddy@0 40 // Token expiration in seconds
paddy@0 41 ExpiresIn int32
paddy@0 42
paddy@0 43 // Requested scope
paddy@0 44 Scope string
paddy@0 45
paddy@0 46 // Redirect URI from request
paddy@0 47 RedirectURI string
paddy@0 48
paddy@0 49 // State data from request
paddy@0 50 State string
paddy@0 51
paddy@0 52 // Date created
paddy@0 53 CreatedAt time.Time
paddy@0 54 }
paddy@0 55
paddy@0 56 // IsExpired is true if authorization expired
paddy@0 57 func (d *AuthorizeData) IsExpired() bool {
paddy@0 58 return d.CreatedAt.Add(time.Duration(d.ExpiresIn) * time.Second).Before(time.Now())
paddy@0 59 }
paddy@0 60
paddy@0 61 // ExpireAt returns the expiration date
paddy@0 62 func (d *AuthorizeData) ExpireAt() time.Time {
paddy@0 63 return d.CreatedAt.Add(time.Duration(d.ExpiresIn) * time.Second)
paddy@0 64 }
paddy@0 65
paddy@0 66 // HandleAuthorizeRequest is the main http.HandlerFunc for handling
paddy@0 67 // authorization requests
paddy@0 68 func HandleAuthorizeRequest(w http.ResponseWriter, r *http.Request, ctx Context) {
paddy@0 69 r.ParseForm()
paddy@0 70
paddy@0 71 requestType := AuthorizeRequestType(r.Form.Get("response_type"))
paddy@0 72 if ctx.Config.AllowedAuthorizeTypes.Exists(requestType) {
paddy@0 73 switch requestType {
paddy@0 74 case CodeAuthRT:
paddy@0 75 handleCodeRequest(w, r, ctx)
paddy@0 76 return
paddy@0 77 case TokenAuthRT:
paddy@0 78 handleTokenRequest(w, r, ctx)
paddy@0 79 return
paddy@0 80 }
paddy@0 81 }
paddy@0 82 // TODO: return error
paddy@0 83 }
paddy@0 84
paddy@0 85 func handleCodeRequest(w http.ResponseWriter, r *http.Request, ctx Context) {
paddy@0 86 // create the authorization request
paddy@0 87 unescapedURI, err := url.QueryUnescape(r.Form.Get("redirect_uri"))
paddy@0 88 if err != nil {
paddy@0 89 unescapedURI = ""
paddy@0 90 }
paddy@0 91 ret := &AuthorizeRequest{
paddy@0 92 Type: CodeAuthRT,
paddy@0 93 State: r.Form.Get("state"),
paddy@0 94 Scope: r.Form.Get("scope"),
paddy@0 95 RedirectURI: unescapedURI,
paddy@0 96 Expiration: ctx.Config.AuthorizationExpiration,
paddy@0 97 }
paddy@0 98
paddy@0 99 // must have a valid client
paddy@0 100 id, err := uuid.Parse(r.Form.Get("client_id"))
paddy@0 101 if err != nil {
paddy@0 102 // TODO: return error
paddy@0 103 return
paddy@0 104 }
paddy@0 105 ret.Client, err = GetClient(id, ctx)
paddy@0 106 if err != nil {
paddy@0 107 // TODO: return error
paddy@0 108 return
paddy@0 109 }
paddy@0 110 if ret.Client.RedirectURI == "" {
paddy@0 111 // TODO: return error
paddy@0 112 return
paddy@0 113 }
paddy@0 114
paddy@0 115 // check redirect uri
paddy@0 116 if ret.RedirectURI == "" {
paddy@0 117 ret.RedirectURI = ret.Client.RedirectURI
paddy@0 118 }
paddy@0 119 if err = ValidateURI(ret.Client.RedirectURI, ret.RedirectURI); err != nil {
paddy@0 120 // TODO: return error
paddy@0 121 return
paddy@0 122 }
paddy@0 123
paddy@0 124 // TODO: do redirect with ret data
paddy@0 125 }
paddy@0 126
paddy@0 127 func handleTokenRequest(w http.ResponseWriter, r *http.Request, ctx Context) {
paddy@0 128 // create the authorization request
paddy@0 129 unescapedURI, err := url.QueryUnescape(r.Form.Get("redirect_uri"))
paddy@0 130 if err != nil {
paddy@0 131 unescapedURI = ""
paddy@0 132 }
paddy@0 133 ret := &AuthorizeRequest{
paddy@0 134 Type: TokenAuthRT,
paddy@0 135 State: r.Form.Get("state"),
paddy@0 136 Scope: r.Form.Get("scope"),
paddy@0 137 RedirectURI: unescapedURI,
paddy@0 138 // this type will generate a token directly, use access token expiration instead.
paddy@0 139 Expiration: ctx.Config.AccessExpiration,
paddy@0 140 }
paddy@0 141
paddy@0 142 // must have a valid client
paddy@0 143 id, err := uuid.Parse(r.Form.Get("client_id"))
paddy@0 144 if err != nil {
paddy@0 145 // TODO: return error
paddy@0 146 return
paddy@0 147 }
paddy@0 148 ret.Client, err = GetClient(id, ctx)
paddy@0 149 if err != nil {
paddy@0 150 // TODO: return error
paddy@0 151 return
paddy@0 152 }
paddy@0 153 if ret.Client.RedirectURI == "" {
paddy@0 154 // TODO: return error
paddy@0 155 return
paddy@0 156 }
paddy@0 157
paddy@0 158 // check redirect uri
paddy@0 159 if ret.RedirectURI == "" {
paddy@0 160 ret.RedirectURI = ret.Client.RedirectURI
paddy@0 161 }
paddy@0 162 if err = ValidateURI(ret.Client.RedirectURI, ret.RedirectURI); err != nil {
paddy@0 163 // TODO: return error
paddy@0 164 }
paddy@0 165
paddy@0 166 // TODO: redirect with ret information
paddy@0 167 }
paddy@0 168
paddy@0 169 func FinishAuthorizeRequest(w http.ResponseWriter, r *http.Request, ar *AuthorizeRequest, ctx Context) {
paddy@0 170 // TODO: check if authorized?
paddy@0 171 if ar.Type == TokenAuthRT {
paddy@0 172 // TODO: w.SetRedirectFragment(true) was called...
paddy@0 173
paddy@0 174 // generate token directly
paddy@0 175 ret := AccessRequest{
paddy@0 176 Code: "",
paddy@0 177 Client: ar.Client,
paddy@0 178 RedirectURI: ar.RedirectURI,
paddy@0 179 Scope: ar.Scope,
paddy@0 180 GenerateRefresh: false, // per the RFC, should NOT generate a refresh token in this case
paddy@0 181 Expiration: ar.Expiration,
paddy@0 182 }
paddy@0 183 // TODO: ret.type was implicit
paddy@0 184 // TODO: ret.Authorized was true
paddy@0 185 FinishAccessRequest(w, r, ret, ctx)
paddy@0 186 } else {
paddy@0 187 // generate authorization token
paddy@0 188 ret := AuthorizeData{
paddy@0 189 Client: ar.Client,
paddy@0 190 CreatedAt: time.Now(),
paddy@0 191 ExpiresIn: ar.Expiration,
paddy@0 192 RedirectURI: ar.RedirectURI,
paddy@0 193 State: ar.State,
paddy@0 194 Scope: ar.Scope,
paddy@0 195 Code: newToken(),
paddy@0 196 }
paddy@0 197
paddy@0 198 // save authorization token
paddy@0 199 err := saveAuthorize(ret, ctx)
paddy@0 200 if err != nil {
paddy@0 201 // TODO: return error
paddy@0 202 return
paddy@0 203 }
paddy@0 204
paddy@0 205 // TODO: redirect with ret.Code and ret.State
paddy@0 206 }
paddy@0 207 }
paddy@0 208
paddy@0 209 func loadAuthorize(code string, ctx Context) (AuthorizeData, error) {
paddy@0 210 return AuthorizeData{}, nil
paddy@0 211 }
paddy@0 212
paddy@0 213 func saveAuthorize(ret AuthorizeData, ctx Context) error {
paddy@0 214 return nil
paddy@0 215 }
paddy@0 216
paddy@0 217 func removeAuthorize(code string, ctx Context) error {
paddy@0 218 return nil
paddy@0 219 }