adding example apps, fixing powerup issues
[feisty_meow.git] / production / example_apps / shared_calendar / src / Traits / GoogleOauthTrait.php
1 <?php
2 namespace App\Traits;
3
4 use Cake\Log\Log;
5 use Google_Client;
6
7 /**
8  * A trait that provides google oauth authorization helper methods. 
9  */
10 trait GoogleOauthTrait
11 {
12     // constants used for items stored in the session.
13     public function POST_AUTHORIZATION_JUMP() { return 'postAuthJump'; }
14     public function STORED_OAUTH_TOKEN() { return 'lastOauthToken'; }
15     public function OAUTH_SCOPES_REQUESTED() { return 'oauthScopes'; }
16     
17     /**
18      * sets the next place in our app to go after authorization.  the url needs to be a link
19      * which can consume the access token after it's available (from getLastOauthToken() below).
20      */
21     public function setPostAuthorizationURL($url)
22     {
23         if (session_status() == PHP_SESSION_NONE) {
24                 Log::debug('starting session for set post auth url');
25                 session_start();
26         }
27         $_SESSION [$this->POST_AUTHORIZATION_JUMP()] = $url;
28     }
29     
30     /**
31      * retrieves any post authorization url that was registered.
32      * this destroys the item in the session also, so it is available for a single use.
33      */
34     public function getPostAuthorizationURL()
35     {
36         if (session_status() == PHP_SESSION_NONE) {
37                 Log::debug('starting session for get post auth url');
38                 session_start();
39         }
40         if (isset ( $_SESSION [$this->POST_AUTHORIZATION_JUMP()] ) && $_SESSION [$this->POST_AUTHORIZATION_JUMP()]) {
41                 $url = $_SESSION [$this->POST_AUTHORIZATION_JUMP()];
42                 Log::debug('loaded url for post auth as: ' . $url);
43                 unset($_SESSION[$this->POST_AUTHORIZATION_JUMP()]);
44                 return $url;
45         } else {
46                 Log::debug('could not find post authorization url in session!');
47                 
48                 return null;
49         }
50     }
51
52     /**
53      * before we can request oauth authorization, we need to know the scopes that will be used
54      * by our application.  this allows them to be set in the session, where scopes should be an
55      * array of valid scope names (defined by the google oauth API).
56      */
57     public function setRequestedScopes($scopes)
58     {
59         if (session_status() == PHP_SESSION_NONE) {
60                 Log::debug('starting session for set req scopes');
61                 session_start();
62         }
63         $_SESSION [$this->OAUTH_SCOPES_REQUESTED()] = $scopes;
64     }
65     
66     /**
67      * gets the array of scopes out of the session for use by the next oauth call.
68      * this will NOT destroy the scopes; that must be done manually with dropRequestedScopes
69      * below, since we need this session item multiple times.
70      */
71     public function getRequestedScopes()
72     {
73         if (session_status() == PHP_SESSION_NONE) {
74                 Log::debug('starting session for get req scopes');              
75                 session_start();
76         }
77         if (isset ( $_SESSION [$this->OAUTH_SCOPES_REQUESTED()] ) && $_SESSION [$this->OAUTH_SCOPES_REQUESTED()]) {
78                 $scopes = $_SESSION [$this->OAUTH_SCOPES_REQUESTED()];
79                 return $scopes;
80         } else {
81                 return null;
82         }
83     }
84     
85     /**
86      * throws the requested scopes out of the session.
87      */
88     public function dropRequestedScopes()
89     {
90         if (session_status() == PHP_SESSION_NONE) {
91                 Log::debug('starting session for drop req scopes');
92                 session_start();
93         }
94         unset($_SESSION[$this->OAUTH_SCOPES_REQUESTED()]);
95     }
96     
97     
98     /**
99      * saves the result of oauth authorization, an access token, into the session.
100      */
101     public function setLastOAuthToken($token)
102     {
103         if (session_status() == PHP_SESSION_NONE) {
104                 Log::debug('starting session for set last oauth token');                
105                 session_start();
106         }
107         $_SESSION [$this->STORED_OAUTH_TOKEN()] = $token;
108     }
109     
110     /**
111      * retrieves the last stored oauth token.  this destroys the item in the session
112      * afterwards, so you get one chance for retrieval of the token.
113      */
114     public function getLastOAuthToken()
115     {
116         if (session_status() == PHP_SESSION_NONE) {
117                 Log::debug('starting session for set last oauth token');                
118                 session_start();
119         }
120         if (isset ( $_SESSION [$this->STORED_OAUTH_TOKEN()] ) && $_SESSION [$this->STORED_OAUTH_TOKEN()]) {
121                 $token = $_SESSION [$this->STORED_OAUTH_TOKEN()];
122                 unset($_SESSION[$this->STORED_OAUTH_TOKEN()]);
123                 return $token;
124         } else {
125                 return null;
126         }
127     }
128     
129     /**
130      * sets up and configures the Google client object using a 'client_secret.json' file
131      * stored in the config directory.  A scope name or an array of scopes should be passed
132      * in the "scopes" parameter.  if the "redirectUri" parameter is not null, then this is set
133      * as the singular redirection URI (which is needed if the client secret has several redirect
134      * URIs listed).
135      */
136     public function createGoogleClient($scopes, $redirectUri = null) {
137         $client = new Google_Client ();
138         
139         // see https://developers.google.com/api-client-library/php/auth/web-app for info on creating the secret.
140         $client->setAuthConfig ( 'config/client_secret.json' );
141         
142         // use the redirect link if they gave us one.
143         if ($redirectUri) {
144                 $client->setRedirectUri ( $redirectUri );
145         }
146         
147         // register for offline access, so we don't need user to be logged in.
148         $client->setAccessType ( "offline" );
149         // enable incremental authorization.
150         $client->setIncludeGrantedScopes ( true );
151         
152         // add the scope(s) we're interested in here.
153         $client->addScope ( $scopes );
154         
155         return $client;
156     }
157     
158     /**
159      * *deprecated* creates a google client configured by discrete parameters.
160      *
161      * note: does not free one to use just any redirect url for login; that's set at credential creation time.
162      */
163     public function createGoogleClientUsingParameters($clientId, $clientSecret, $applicationName, $redirectUri, $scopes) {
164         $client = new Google_Client ();
165         
166         // see https://developers.google.com/api-client-library/php/auth/web-app for info on creating the secret.
167         $client->setClientId ( $clientId );
168         $client->setClientSecret ( $clientSecret );
169         $client->setApplicationName ( $applicationName );
170         $client->setRedirectUri ( $redirectUri );
171         
172         // register for offline access, so we don't need user to be logged in.
173         $client->setAccessType ( "offline" );
174         // enable incremental authorization.
175         $client->setIncludeGrantedScopes ( true );
176         
177         // add the scope we're interested in here.
178         $client->addScope ( $scopes);
179         
180         return $client;
181     }
182     
183     /**
184      * checks whether the google client object's token is still valid.  if not, the token is refreshed.
185      * this will only work with tokens that were originally requested for offline access and that possess
186      * the refresh token.
187      * if the refresh was done, the newly revitalized token is returned and must be stored.
188      * if the token is already okay, then null is returned (which avoids tricky comparisons to
189      * determine if an update happened).
190      */
191     public function freshenTokenInClient($client) 
192     {
193         if (! $client->isAccessTokenExpired ()) {
194                 // no refresh needed.
195                 return null;
196         }
197         // currently assuming the new access token also contains the old refresh token.
198         // this has been borne out by results from google.
199         Log::debug ( 'noticed that the access token has expired!' );
200         
201         // retrieve a new access token using our refresh token.
202         $refresher = $client->getRefreshToken ();
203         $client->fetchAccessTokenWithRefreshToken ( $refresher );
204         $accessToken = $client->getAccessToken ();
205         
206         //bad! do not show this in real app.
207         //Log::debug ( 'got a new access token after refresh: ' . var_export ( $accessToken, true ) );
208         
209         // return the new and tasty token.
210         return $accessToken;
211     }
212     
213     /**
214      * throws out the specified access token, which means that the app will have to reauthorize
215      * to get another token.
216      */
217     public function revokeToken($token)
218     {
219         $client = new Google_Client ();
220         // apparently the client needs very little setup to perform this if we have the whole token?
221         $client->revokeToken ( $token );
222     }
223 }
224
225 /*
226  * hmmm: still need a method for doing additive authorizations, where we request new scopes and
227  * have them bundled onto our existing access token.  would need to store the token afterwards also.
228  */
229
230