github.com/aarzilli/tools@v0.0.0-20151123112009-0d27094f75e0/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 ) 84 85 // User information. 86 type User struct { 87 ID string 88 Email string 89 Name string 90 EmailVerified bool 91 } 92 93 // Key used to store the user information in the current session. 94 type SessionUserKey int 95 96 const sessionUserKey SessionUserKey = 0 97 98 // SignedIn is on top of LoggedIn. 99 // Can only be measured by presence of cookie gtokenCookieName 100 func IsSignedIn(r *http.Request) bool { 101 signedIn := false 102 cks := r.Cookies() 103 for _, ck := range cks { 104 if ck.Name == gtokenCookieName { 105 signedIn = true 106 break 107 } 108 } 109 return signedIn 110 } 111 112 // 113 // CurrentUser extracts the user information stored in current session. 114 // 115 // If there is no existing session, identity toolkit token is checked. 116 // If the token is valid, a new session is created. 117 // 118 // If any error happens, nil is returned. 119 func CurrentUser(r *http.Request) *User { 120 c := appengine.NewContext(r) 121 sess, _ := cookieStore.Get(r, sessionName) 122 if sess.IsNew { 123 // Create an identity toolkit client associated with the GAE context. 124 client, err := gitkit.NewWithContext(c, gitkitClient) 125 if err != nil { 126 aelog.Errorf(c, "Failed to create a gitkit.Client with a context: %s", err) 127 return nil 128 } 129 // Extract the token string from request. 130 ts := client.TokenFromRequest(r) 131 if ts == "" { 132 return nil 133 } 134 // Check the token issue time. Only accept token that is no more than 15 135 // minitues old even if it's still valid. 136 token, err := client.ValidateToken(ts) 137 if err != nil { 138 aelog.Errorf(c, "Invalid token %s: %s", ts, err) 139 return nil 140 } 141 if time.Now().Sub(token.IssueAt) > maxTokenAge*time.Second { 142 aelog.Infof(c, "Token %s is too old. Issused at: %s", ts, token.IssueAt) 143 return nil 144 } 145 146 // Fetch user info. 147 u, err := client.UserByLocalID(token.LocalID) 148 if err != nil { 149 aelog.Errorf(c, "Failed to fetch user info for %s[%s]: %s", token.Email, token.LocalID, err) 150 return nil 151 } 152 return &User{ 153 ID: u.LocalID, 154 Email: u.Email, 155 Name: u.DisplayName, 156 EmailVerified: u.EmailVerified, 157 } 158 } else { 159 // Extracts user from current session. 160 v, ok := sess.Values[sessionUserKey] 161 if !ok { 162 aelog.Errorf(c, "no user found in current session") 163 } 164 return v.(*User) 165 } 166 } 167 168 // saveCurrentUser stores the user information in current session. 169 func saveCurrentUser(r *http.Request, w http.ResponseWriter, u *User) { 170 if u == nil { 171 return 172 } 173 sess, _ := cookieStore.Get(r, sessionName) 174 sess.Values[sessionUserKey] = *u 175 err := sess.Save(r, w) 176 if err != nil { 177 aelog.Errorf(appengine.NewContext(r), "Cannot save session: %s", err) 178 } 179 }