github.com/pbberlin/tools@v0.0.0-20160910141205-7aa5421c2169/appengine/login/gitkit1/gitkit1.go (about) 1 // package gitkit1 omits gitkit functions for updating user data or account-deletion; 2 // instead all app-specific settings and the redirect-urls are customizable. 3 package gitkit1 4 5 // Code taken from 6 // https://github.com/googlesamples/identity-toolkit-go/tree/master/favweekday 7 // 8 // The complete concept is expained here: 9 // https://developers.google.com/identity/choose-auth 10 // https://developers.google.com/identity/toolkit/web/federated-login 11 // 12 // https://developers.google.com/identity/toolkit/web/configure-service 13 // https://developers.google.com/identity/toolkit/web/setup-frontend 14 // 15 // 16 // Remove apps: 17 // https://security.google.com/settings/security/permissions 18 // https://www.facebook.com/settings?tab=applications 19 20 import ( 21 "net/http" 22 "time" 23 24 // issues certificates (tokens) for possible http requests, making other requests impossible 25 26 "github.com/google/identity-toolkit-go-client/gitkit" 27 "github.com/gorilla/sessions" 28 29 "google.golang.org/appengine" 30 aelog "google.golang.org/appengine/log" 31 ) 32 33 // Action URLs. 34 // These need to be updated 35 // https://console.developers.google.com/project/tec-news/apiui/credential 36 // https://console.developers.google.com/project/tec-news/apiui/apiview/identitytoolkit/identity_toolkit 37 // https://developers.facebook.com/apps/942324259171809/settings/advanced/ 38 39 const ( 40 homeURL = "/auth" 41 42 WidgetSigninAuthorizedRedirectURL = "/auth/authorized-redirect" // THIS one needs to be registered all over 43 signOutURL = "/auth/signout" 44 accountChooserBrandingURL = "/auth/accountChooserBranding.html" 45 46 oobActionURL = "/auth/send-email" // not needed, but kept 47 ) 48 49 var ( 50 signinLandingDefaultURL = "/auth/signin-default-landing" 51 signoutLandingDefaultURL = "/auth/signout-default-landing" 52 // successLandingURL = "/auth/signin-landing" 53 // signoutLandingURL = "/auth/signout-landing" 54 ) 55 56 // Cookie/Form input names. 57 const ( 58 59 // contains jws from appengine/user.CurrentUser() ...; 60 // not used here 61 aeUserSessName = "SACSID" 62 63 // = cookie name; 64 // contains jwt from google/facebook/twitter; 65 // remains, even when "signed out" 66 // remains, even when logging out of google/twitter 67 // cannot be overwritten by "eraser" 68 sessionName = "SESSIONID" 69 70 // Created on top of sessionName on "signin" 71 // Remains 72 gtokenCookieName = "gtoken" 73 74 maxTokenAge = 1200 // 20 minutes 75 76 maxSessionIDAge = 1800 77 ) 78 79 var ( 80 xsrfKey string 81 cookieStore *sessions.CookieStore 82 gitkitClient *gitkit.Client 83 client *gitkit.Client 84 ) 85 86 // User information. 87 type User struct { 88 ID string 89 Email string 90 Name string 91 EmailVerified bool 92 } 93 94 // Key used to store the user information in the current session. 95 type SessionUserKey int 96 97 const sessionUserKey SessionUserKey = 0 98 99 // SignedIn is on top of LoggedIn. 100 // Can only be measured by presence of cookie gtokenCookieName 101 func IsSignedIn(r *http.Request) bool { 102 signedIn := false 103 cks := r.Cookies() 104 for _, ck := range cks { 105 if ck.Name == gtokenCookieName { 106 signedIn = true 107 break 108 } 109 } 110 return signedIn 111 } 112 113 // 114 // CurrentUser extracts the user information stored in current session. 115 // 116 // If there is no existing session, identity toolkit token is checked. 117 // If the token is valid, a new session is created. 118 // 119 // If any error happens, nil is returned. 120 func CurrentUser(r *http.Request) *User { 121 c := appengine.NewContext(r) 122 sess, _ := cookieStore.Get(r, sessionName) 123 if sess.IsNew { 124 // Create an identity toolkit client associated with the GAE context. 125 // client, err := gitkit.NewWithContext(c, gitkitClient) 126 // if err != nil { 127 // aelog.Errorf(c, "Failed to create a gitkit.Client with a context: %s", err) 128 // return nil 129 // } 130 // Extract the token string from request. 131 ts := client.TokenFromRequest(r) 132 if ts == "" { 133 return nil 134 } 135 // Check the token issue time. Only accept token that is no more than 15 136 // minutes old even if it's still valid. 137 // token, err := client.ValidateToken(ts) 138 token, err := client.ValidateToken(appengine.NewContext(r), ts, []string{clientID}) 139 if err != nil { 140 aelog.Errorf(c, "Invalid token %s: %s", ts, err) 141 return nil 142 } 143 if time.Now().Sub(token.IssueAt) > maxTokenAge*time.Second { 144 aelog.Infof(c, "Token %s is too old. Issused at: %s", ts, token.IssueAt) 145 return nil 146 } 147 148 // Fetch user info. 149 u, err := client.UserByLocalID(token.LocalID) 150 if err != nil { 151 aelog.Errorf(c, "Failed to fetch user info for %s[%s]: %s", token.Email, token.LocalID, err) 152 return nil 153 } 154 return &User{ 155 ID: u.LocalID, 156 Email: u.Email, 157 Name: u.DisplayName, 158 EmailVerified: u.EmailVerified, 159 } 160 } else { 161 // Extracts user from current session. 162 v, ok := sess.Values[sessionUserKey] 163 if !ok { 164 aelog.Errorf(c, "no user found in current session") 165 } 166 return v.(*User) 167 } 168 } 169 170 // saveCurrentUser stores the user information in current session. 171 func saveCurrentUser(r *http.Request, w http.ResponseWriter, u *User) { 172 if u == nil { 173 return 174 } 175 sess, _ := cookieStore.Get(r, sessionName) 176 sess.Values[sessionUserKey] = *u 177 err := sess.Save(r, w) 178 if err != nil { 179 aelog.Errorf(appengine.NewContext(r), "Cannot save session: %s", err) 180 } 181 }