├── .actionScriptProperties ├── .flexLibProperties ├── .gitignore ├── .project ├── LICENSE ├── README.md ├── libs ├── as3commons-logging-2.7.swc └── as3corelib-0.93.swc └── src ├── main └── actionscript │ └── com │ └── adobe │ └── protocols │ └── oauth2 │ ├── OAuth2.as │ ├── OAuth2Const.as │ ├── event │ ├── GetAccessTokenEvent.as │ ├── IOAuth2Event.as │ └── RefreshAccessTokenEvent.as │ └── grant │ ├── AuthorizationCodeGrant.as │ ├── IGrantType.as │ ├── ImplicitGrant.as │ └── ResourceOwnerCredentialsGrant.as └── test └── actionscript └── com └── adobe └── protocols └── oauth2 └── OAuth2Test.as /.actionScriptProperties: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /.flexLibProperties: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .settings 2 | bin/* 3 | bin-debug/* 4 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | actionscript-oauth2 4 | 5 | 6 | 7 | 8 | 9 | com.adobe.flexbuilder.project.flexbuilder 10 | 11 | 12 | 13 | 14 | 15 | com.adobe.flexbuilder.project.flexlibnature 16 | com.adobe.flexbuilder.project.actionscriptnature 17 | 18 | 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2012 Charles Bihis 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ActionScript OAuth 2.0 Library 2 | 3 | An ActionScript 3 library for interfacing with OAuth 2.0 services, implemented according to the [OAuth 2.0 v2.15 specification](http://tools.ietf.org/html/draft-ietf-oauth-v2-15). 4 | 5 | ## Overview 6 | 7 | This library is built for use with Flash/Flex/AIR projects to facilitate communication with OAuth 2.0 services. It provides mechanisms to authenticate against OAuth 2.0 servers using all standard authentication and authorization workflows. 8 | 9 | This project was first mentioned in my blog post at http://blogs.adobe.com/charles/2012/05/oauth-2-0-library-for-actionscript.html. 10 | 11 | ### Features 12 | 13 | The ActionScript OAuth 2.0 Library supports the following features... 14 | 15 | * Ability to [fetch an access token](http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-4) via the OAuth 2.0 supported workflows... 16 | * [Authorization Code Grant workflow](http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-4.1) 17 | * [Implicit Grant workflow](http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-4.2) 18 | * [Resource Owner Password Credentials workflow](http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-4.3) 19 | * Ability to [refresh an access token](http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-6) 20 | * Robust and adjustable logging 21 | * Ability to log in, view, and interact with the user consent page within a given StageWebView object 22 | 23 | ### Dependencies 24 | 25 | * [as3corelib](https://github.com/mikechambers/as3corelib) 26 | * [as3commons-logging](http://code.google.com/p/as3-commons/) 27 | 28 | ## Reference 29 | 30 | ### Usage 31 | 32 | To use the library, simply drop in the SWC (or the source) into your project, along with the appropriate dependencies, and follow the usage below... 33 | 34 | // set up our StageWebView object to use our visible stage 35 | stageWebView.stage = stage; 36 | 37 | // set up the call 38 | var oauth2:OAuth2 = new OAuth2("https://accounts.google.com/o/oauth2/auth", "https://accounts.google.com/o/oauth2/token", LogSetupLevel.ALL); 39 | var grant:IGrantType = new AuthorizationCodeGrant(stageWebView, // the StageWebView object for which to display the user consent screen 40 | "INSERT_CLIENT_ID_HERE", // your client ID 41 | "INSERT_CLIENT_SECRET_HERE", // your client secret 42 | "INSERT_REDIRECT_URI_HERE", // your redirect URI 43 | "INSERT_SCOPE_HERE", // (optional) your scope 44 | "INSERT_STATE_HERE"); // (optional) your state 45 | 46 | // make the call 47 | oauth2.addEventListener(GetAccessTokenEvent.TYPE, onGetAccessToken); 48 | oauth2.getAccessToken(grant); 49 | 50 | function onGetAccessToken(getAccessTokenEvent:GetAccessTokenEvent):void 51 | { 52 | if (getAccessTokenEvent.errorCode == null && getAccessTokenEvent.errorMessage == null) 53 | { 54 | // success! 55 | trace("Your access token value is: " + getAccessTokenEvent.accessToken); 56 | } 57 | else 58 | { 59 | // fail :( 60 | } 61 | } // onGetAccessToken 62 | 63 | ### Demo 64 | 65 | * Demo description: http://blogs.adobe.com/charles/2012/05/oauth-2-0-library-for-actionscript.html 66 | * Demo source: https://github.com/charlesbihis/sandbox/tree/master/actionscript/actionscript-oauth2-mobile-demo 67 | 68 | ### Documentation 69 | 70 | You can find the full ASDocs for the project [here](http://charlesbihis.github.com/actionscript-oauth2/docs/). 71 | 72 | ## Author 73 | 74 | * Created by Charles Bihis 75 | * Website: [www.whoischarles.com](http://www.whoischarles.com) 76 | * E-mail: [charles@whoischarles.com](mailto:charles@whoischarles.com) 77 | * Twitter: [@charlesbihis](http://www.twitter.com/charlesbihis) 78 | 79 | ## License 80 | 81 | The ActionScript OAuth 2.0 Library is licensed under the [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0). -------------------------------------------------------------------------------- /libs/as3commons-logging-2.7.swc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charlesbihis/actionscript-oauth2/285ca024d99ad693a4c92288418bf7368dd41560/libs/as3commons-logging-2.7.swc -------------------------------------------------------------------------------- /libs/as3corelib-0.93.swc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charlesbihis/actionscript-oauth2/285ca024d99ad693a4c92288418bf7368dd41560/libs/as3corelib-0.93.swc -------------------------------------------------------------------------------- /src/main/actionscript/com/adobe/protocols/oauth2/OAuth2.as: -------------------------------------------------------------------------------- 1 | package com.adobe.protocols.oauth2 2 | { 3 | import com.adobe.protocols.oauth2.event.GetAccessTokenEvent; 4 | import com.adobe.protocols.oauth2.event.RefreshAccessTokenEvent; 5 | import com.adobe.protocols.oauth2.grant.AuthorizationCodeGrant; 6 | import com.adobe.protocols.oauth2.grant.IGrantType; 7 | import com.adobe.protocols.oauth2.grant.ImplicitGrant; 8 | import com.adobe.protocols.oauth2.grant.ResourceOwnerCredentialsGrant; 9 | import com.adobe.serialization.json.JSONParseError; 10 | 11 | import flash.events.ErrorEvent; 12 | import flash.events.Event; 13 | import flash.events.EventDispatcher; 14 | import flash.events.IOErrorEvent; 15 | import flash.events.LocationChangeEvent; 16 | import flash.events.SecurityErrorEvent; 17 | import flash.net.URLLoader; 18 | import flash.net.URLRequest; 19 | import flash.net.URLRequestMethod; 20 | import flash.net.URLVariables; 21 | 22 | import org.as3commons.logging.api.ILogger; 23 | import org.as3commons.logging.api.LOGGER_FACTORY; 24 | import org.as3commons.logging.api.getLogger; 25 | import org.as3commons.logging.setup.LevelTargetSetup; 26 | import org.as3commons.logging.setup.LogSetupLevel; 27 | import org.as3commons.logging.setup.target.TraceTarget; 28 | 29 | /** 30 | * Event that is broadcast when results from a getAccessToken request are received. 31 | * 32 | * @eventType com.adobe.protocols.oauth2.event.GetAccessTokenEvent.TYPE 33 | * 34 | * @see #getAccessToken() 35 | * @see com.adobe.protocols.oauth2.event.GetAccessTokenEvent 36 | */ 37 | [Event(name="getAccessToken", type="com.adobe.protocols.oauth2.event.GetAccessTokenEvent")] 38 | 39 | /** 40 | * Event that is broadcast when results from a refreshAccessToken request are received. 41 | * 42 | * @eventType com.adobe.protocols.oauth2.event.RefreshAccessTokenEvent.TYPE 43 | * 44 | * @see #refreshAccessToken() 45 | * @see com.adobe.protocols.oauth2.event.RefreshAccessTokenEvent 46 | */ 47 | [Event(name="refreshAccessToken", type="com.adobe.protocols.oauth2.event.RefreshAccessTokenEvent")] 48 | 49 | /** 50 | * Utility class the encapsulates APIs for interaction with an OAuth 2.0 server. 51 | * Implemented against the OAuth 2.0 v2.15 specification. 52 | * 53 | * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-15 54 | * 55 | * @author Charles Bihis (www.whoischarles.com) 56 | */ 57 | public class OAuth2 extends EventDispatcher 58 | { 59 | private static const log:ILogger = getLogger(OAuth2); 60 | 61 | private var grantType:IGrantType; 62 | private var authEndpoint:String; 63 | private var tokenEndpoint:String; 64 | private var traceTarget:TraceTarget = new TraceTarget(); 65 | 66 | 67 | /** 68 | * Constructor to create a valid OAuth2 client object. 69 | * 70 | * @param authEndpoint The authorization endpoint used by the OAuth 2.0 server 71 | * @param tokenEndpoint The token endpoint used by the OAuth 2.0 server 72 | * @param logLevel (Optional) The new log level for the logger to use 73 | */ 74 | public function OAuth2(authEndpoint:String, tokenEndpoint:String, logLevel:LogSetupLevel = null) 75 | { 76 | // save endpoint properties 77 | this.authEndpoint = authEndpoint; 78 | this.tokenEndpoint = tokenEndpoint; 79 | 80 | // set up logging 81 | traceTarget = new TraceTarget(); 82 | traceTarget.format = "{date} {time} [{logLevel}] {name} {message}"; 83 | LOGGER_FACTORY.setup = new LevelTargetSetup(traceTarget, (logLevel == null) ? LogSetupLevel.NONE : logLevel); 84 | } // OAuth2 85 | 86 | /** 87 | * Initiates the access token request workflow with the proper context as 88 | * described by the passed-in grant-type object. Upon completion, will 89 | * dispatch a GetAccessTokenEvent event. 90 | * 91 | * @param grantType An IGrantType object which represents the desired workflow to use when requesting an access token 92 | * 93 | * @see com.adobe.protocols.oauth2.grant.IGrantType 94 | * @see com.adobe.protocols.oauth2.event.GetAccessTokenEvent#TYPE 95 | */ 96 | public function getAccessToken(grantType:IGrantType):void 97 | { 98 | if (grantType is AuthorizationCodeGrant) 99 | { 100 | log.info("Initiating getAccessToken() with authorization code grant type workflow"); 101 | getAccessTokenWithAuthorizationCodeGrant(grantType as AuthorizationCodeGrant); 102 | } // if statement 103 | else if (grantType is ImplicitGrant) 104 | { 105 | log.info("Initiating getAccessToken() with implicit grant type workflow"); 106 | getAccessTokenWithImplicitGrant(grantType as ImplicitGrant); 107 | } // else-if statement 108 | else if (grantType is ResourceOwnerCredentialsGrant) 109 | { 110 | log.info("Initiating getAccessToken() with resource owner credentials grant type workflow"); 111 | getAccessTokenWithResourceOwnerCredentialsGrant(grantType as ResourceOwnerCredentialsGrant); 112 | } // else-if statement 113 | } // getAccessToken 114 | 115 | /** 116 | * Initiates request to refresh a given access token. Upon completion, will dispatch 117 | * a RefreshAccessTokenEvent event. On success, a new refresh token may 118 | * be issues, at which point the client should discard the old refresh token with the 119 | * new one. 120 | * 121 | * @param refreshToken A valid refresh token received during last request for an access token 122 | * @param clientId The client identifier 123 | * @param clientSecret The client secret 124 | * 125 | * @see com.adobe.protocols.oauth2.event.RefreshAccessTokenEvent#TYPE 126 | */ 127 | public function refreshAccessToken(refreshToken:String, clientId:String, clientSecret:String, scope:String = null):void 128 | { 129 | // create result event 130 | var refreshAccessTokenEvent:RefreshAccessTokenEvent = new RefreshAccessTokenEvent(); 131 | 132 | // set up URL request 133 | var urlRequest:URLRequest = new URLRequest(tokenEndpoint); 134 | var urlLoader:URLLoader = new URLLoader(); 135 | urlRequest.method = URLRequestMethod.POST; 136 | 137 | // define POST parameters 138 | var urlVariables : URLVariables = new URLVariables(); 139 | urlVariables.grant_type = OAuth2Const.GRANT_TYPE_REFRESH_TOKEN; 140 | urlVariables.client_id = clientId; 141 | urlVariables.client_secret = clientSecret; 142 | urlVariables.refresh_token = refreshToken; 143 | 144 | // define optional scope parameter only when scope not null 145 | if(scope !== null) 146 | { 147 | urlVariables.scope = scope; 148 | } 149 | 150 | urlRequest.data = urlVariables; 151 | 152 | // attach event listeners 153 | urlLoader.addEventListener(Event.COMPLETE, onRefreshAccessTokenResult); 154 | urlLoader.addEventListener(IOErrorEvent.IO_ERROR, onRefreshAccessTokenError); 155 | urlLoader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onRefreshAccessTokenError); 156 | 157 | // make the call 158 | try 159 | { 160 | urlLoader.load(urlRequest); 161 | } // try statement 162 | catch (error:Error) 163 | { 164 | log.error("Error loading token endpoint \"" + tokenEndpoint + "\""); 165 | } // catch statement 166 | 167 | function onRefreshAccessTokenResult(event:Event):void 168 | { 169 | try 170 | { 171 | var response:Object = com.adobe.serialization.json.JSON.decode(event.target.data); 172 | log.debug("Access token: " + response.access_token); 173 | refreshAccessTokenEvent.parseAccessTokenResponse(response); 174 | } // try statement 175 | catch (error:JSONParseError) 176 | { 177 | refreshAccessTokenEvent.errorCode = "com.adobe.serialization.json.JSONParseError"; 178 | refreshAccessTokenEvent.errorMessage = "Error parsing output from refresh access token response"; 179 | } // catch statement 180 | 181 | dispatchEvent(refreshAccessTokenEvent); 182 | } // onRefreshAccessTokenResult 183 | 184 | function onRefreshAccessTokenError(event:Event):void 185 | { 186 | log.error("Error encountered during refresh access token request: " + event); 187 | 188 | try 189 | { 190 | var error:Object = com.adobe.serialization.json.JSON.decode(event.target.data); 191 | refreshAccessTokenEvent.errorCode = error.error; 192 | refreshAccessTokenEvent.errorMessage = error.error_description; 193 | } // try statement 194 | catch (error:JSONParseError) 195 | { 196 | refreshAccessTokenEvent.errorCode = "Unknown"; 197 | refreshAccessTokenEvent.errorMessage = "Error encountered during refresh access token request. Unable to parse error message."; 198 | } // catch statement 199 | 200 | dispatchEvent(refreshAccessTokenEvent); 201 | } // onRefreshAccessTokenError 202 | } // refreshAccessToken 203 | 204 | /** 205 | * Modifies the log level of the logger at runtime. 206 | * 207 | *

By default, logging is turned off. Passing in any value will modify the logging level 208 | * of the application. This method can accept any of the following values...

209 | * 210 | * 224 | * 225 | * @param logLevel The new log level for the logger to use 226 | * 227 | * @see org.as3commons.logging.setup.LogSetupLevel.NONE 228 | * @see org.as3commons.logging.setup.LogSetupLevel.FATAL 229 | * @see org.as3commons.logging.setup.LogSetupLevel.FATAL_ONLY 230 | * @see org.as3commons.logging.setup.LogSetupLevel.ERROR 231 | * @see org.as3commons.logging.setup.LogSetupLevel.ERROR_ONLY 232 | * @see org.as3commons.logging.setup.LogSetupLevel.WARN 233 | * @see org.as3commons.logging.setup.LogSetupLevel.WARN_ONLY 234 | * @see org.as3commons.logging.setup.LogSetupLevel.INFO 235 | * @see org.as3commons.logging.setup.LogSetupLevel.INFO_ONLY 236 | * @see org.as3commons.logging.setup.LogSetupLevel.DEBUG 237 | * @see org.as3commons.logging.setup.LogSetupLevel.DEBUG_ONLY 238 | * @see org.as3commons.logging.setup.LogSetupLevel.ALL 239 | */ 240 | public function setLogLevel(logLevel:LogSetupLevel):void 241 | { 242 | LOGGER_FACTORY.setup = new LevelTargetSetup(traceTarget, logLevel); 243 | } // setLogLevel 244 | 245 | /** 246 | * @private 247 | * 248 | * Helper function that completes get-access-token request using the authorization code grant type. 249 | */ 250 | private function getAccessTokenWithAuthorizationCodeGrant(authorizationCodeGrant:AuthorizationCodeGrant):void 251 | { 252 | // create result event 253 | var getAccessTokenEvent:GetAccessTokenEvent = new GetAccessTokenEvent(); 254 | 255 | // add event listeners 256 | authorizationCodeGrant.stageWebView.addEventListener(LocationChangeEvent.LOCATION_CHANGING, onLocationChanging); 257 | authorizationCodeGrant.stageWebView.addEventListener(LocationChangeEvent.LOCATION_CHANGE, onLocationChanging); 258 | authorizationCodeGrant.stageWebView.addEventListener(Event.COMPLETE, onStageWebViewComplete); 259 | authorizationCodeGrant.stageWebView.addEventListener(ErrorEvent.ERROR, onStageWebViewError); 260 | 261 | // start the auth process 262 | var startTime:Number = new Date().time; 263 | log.info("Loading auth URL: " + authorizationCodeGrant.getFullAuthUrl(authEndpoint)); 264 | authorizationCodeGrant.stageWebView.loadURL(authorizationCodeGrant.getFullAuthUrl(authEndpoint)); 265 | 266 | function onLocationChanging(locationChangeEvent:LocationChangeEvent):void 267 | { 268 | log.info("Loading URL: " + locationChangeEvent.location); 269 | if (locationChangeEvent.location.indexOf(authorizationCodeGrant.redirectUri) == 0 && locationChangeEvent.location.indexOf(OAuth2Const.RESPONSE_PROPERTY_AUTHORIZATION_CODE) > 0) 270 | { 271 | log.info("Redirect URI encountered (" + authorizationCodeGrant.redirectUri + "). Extracting values from path."); 272 | 273 | // stop event from propogating 274 | locationChangeEvent.preventDefault(); 275 | 276 | // determine if authorization was successful 277 | var queryParams:Object = extractQueryParams(locationChangeEvent.location); 278 | var code:String = queryParams.code; // authorization code 279 | if (code != null) 280 | { 281 | log.debug("Authorization code: " + code); 282 | getAccessTokenWithAuthCode(code); 283 | } // if statement 284 | else 285 | { 286 | log.error("Error encountered during authorization request"); 287 | getAccessTokenEvent.errorCode = queryParams.error; 288 | getAccessTokenEvent.errorMessage = queryParams.error_description; 289 | dispatchEvent(getAccessTokenEvent); 290 | } // else statement 291 | } // if statement 292 | } // onLocationChange 293 | 294 | function getAccessTokenWithAuthCode(code:String):void 295 | { 296 | // set up URL request 297 | var urlRequest:URLRequest = new URLRequest(tokenEndpoint); 298 | var urlLoader:URLLoader = new URLLoader(); 299 | urlRequest.method = URLRequestMethod.POST; 300 | 301 | // define POST parameters 302 | var urlVariables : URLVariables = new URLVariables(); 303 | urlVariables.grant_type = OAuth2Const.GRANT_TYPE_AUTHORIZATION_CODE; 304 | urlVariables.code = code; 305 | urlVariables.redirect_uri = authorizationCodeGrant.redirectUri; 306 | urlVariables.client_id = authorizationCodeGrant.clientId; 307 | urlVariables.client_secret = authorizationCodeGrant.clientSecret; 308 | urlRequest.data = urlVariables; 309 | 310 | // attach event listeners 311 | urlLoader.addEventListener(Event.COMPLETE, onGetAccessTokenResult); 312 | urlLoader.addEventListener(IOErrorEvent.IO_ERROR, onGetAccessTokenError); 313 | urlLoader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onGetAccessTokenError); 314 | 315 | // make the call 316 | try 317 | { 318 | urlLoader.load(urlRequest); 319 | } // try statement 320 | catch (error:Error) 321 | { 322 | log.error("Error loading token endpoint \"" + tokenEndpoint + "\""); 323 | } // catch statement 324 | 325 | function onGetAccessTokenResult(event:Event):void 326 | { 327 | try 328 | { 329 | var response:Object = com.adobe.serialization.json.JSON.decode(event.target.data); 330 | log.debug("Access token: " + response.access_token); 331 | getAccessTokenEvent.parseAccessTokenResponse(response); 332 | } // try statement 333 | catch (error:JSONParseError) 334 | { 335 | getAccessTokenEvent.errorCode = "com.adobe.serialization.json.JSONParseError"; 336 | getAccessTokenEvent.errorMessage = "Error parsing output from access token response"; 337 | } // catch statement 338 | 339 | dispatchEvent(getAccessTokenEvent); 340 | } // onGetAccessTokenResult 341 | 342 | function onGetAccessTokenError(event:Event):void 343 | { 344 | log.error("Error encountered during access token request: " + event); 345 | 346 | try 347 | { 348 | var error:Object = com.adobe.serialization.json.JSON.decode(event.target.data); 349 | getAccessTokenEvent.errorCode = error.error; 350 | getAccessTokenEvent.errorMessage = error.error_description; 351 | } // try statement 352 | catch (error:JSONParseError) 353 | { 354 | getAccessTokenEvent.errorCode = "Unknown"; 355 | getAccessTokenEvent.errorMessage = "Error encountered during access token request. Unable to parse error message."; 356 | } // catch statement 357 | 358 | dispatchEvent(getAccessTokenEvent); 359 | } // onGetAccessTokenError 360 | } // getAccessTokenWithAuthCode 361 | 362 | function onStageWebViewComplete(event:Event):void 363 | { 364 | // Note: Special provision made particularly for Google OAuth 2 implementation for installed 365 | // applications. Particularly, when we see a certain redirect URI, we must look for the authorization 366 | // code in the page title as opposed to in the URL. See https://developers.google.com/accounts/docs/OAuth2InstalledApp#choosingredirecturi 367 | // for more information. 368 | if (authorizationCodeGrant.redirectUri == OAuth2Const.GOOGLE_INSTALLED_APPLICATION_REDIRECT_URI && event.currentTarget.title.indexOf(OAuth2Const.RESPONSE_TYPE_AUTHORIZATION_CODE) > 0) 369 | { 370 | var codeString:String = event.currentTarget.title.substring(event.currentTarget.title.indexOf(OAuth2Const.RESPONSE_TYPE_AUTHORIZATION_CODE)); 371 | var code:String = codeString.split("=")[1]; 372 | log.debug("Authorization code extracted from page title: " + code); 373 | getAccessTokenWithAuthCode(code); 374 | } 375 | else 376 | { 377 | log.info("Auth URL loading complete after " + (new Date().time - startTime) + "ms"); 378 | } 379 | } // onStageWebViewComplete 380 | 381 | function onStageWebViewError(errorEvent:ErrorEvent):void 382 | { 383 | log.error("Error occurred with StageWebView: " + errorEvent); 384 | getAccessTokenEvent.errorCode = "STAGE_WEB_VIEW_ERROR"; 385 | getAccessTokenEvent.errorMessage = "Error occurred with StageWebView"; 386 | dispatchEvent(getAccessTokenEvent); 387 | } // onStageWebViewError 388 | } // getAccessTokenWithAuthorizationCodeGrant 389 | 390 | /** 391 | * @private 392 | * 393 | * Helper function that completes get-access-token request using the implicit grant type. 394 | */ 395 | private function getAccessTokenWithImplicitGrant(implicitGrant:ImplicitGrant):void 396 | { 397 | // create result event 398 | var getAccessTokenEvent:GetAccessTokenEvent = new GetAccessTokenEvent(); 399 | 400 | // add event listeners 401 | implicitGrant.stageWebView.addEventListener(LocationChangeEvent.LOCATION_CHANGING, onLocationChange); 402 | implicitGrant.stageWebView.addEventListener(LocationChangeEvent.LOCATION_CHANGE, onLocationChange); 403 | implicitGrant.stageWebView.addEventListener(ErrorEvent.ERROR, onStageWebViewError); 404 | 405 | // start the auth process 406 | log.info("Loading auth URL: " + implicitGrant.getFullAuthUrl(authEndpoint)); 407 | implicitGrant.stageWebView.loadURL(implicitGrant.getFullAuthUrl(authEndpoint)); 408 | 409 | function onLocationChange(locationChangeEvent:LocationChangeEvent):void 410 | { 411 | log.info("Loading URL: " + locationChangeEvent.location); 412 | if (locationChangeEvent.location.indexOf(implicitGrant.redirectUri) == 0 && locationChangeEvent.location.indexOf(OAuth2Const.RESPONSE_PROPERTY_ACCESS_TOKEN) > 0) 413 | { 414 | log.info("Redirect URI encountered (" + implicitGrant.redirectUri + "). Extracting values from path."); 415 | 416 | // stop event from propogating 417 | locationChangeEvent.preventDefault(); 418 | 419 | // determine if authorization was successful 420 | var queryParams:Object = extractQueryParams(locationChangeEvent.location); 421 | var accessToken:String = queryParams.access_token; 422 | if (accessToken != null) 423 | { 424 | log.debug("Access token: " + accessToken); 425 | getAccessTokenEvent.parseAccessTokenResponse(queryParams); 426 | dispatchEvent(getAccessTokenEvent); 427 | } // if statement 428 | else 429 | { 430 | log.error("Error encountered during access token request"); 431 | getAccessTokenEvent.errorCode = queryParams.error; 432 | getAccessTokenEvent.errorMessage = queryParams.error_description; 433 | dispatchEvent(getAccessTokenEvent); 434 | } // else statement 435 | } // if statement 436 | } // onLocationChange 437 | 438 | function onStageWebViewError(errorEvent:ErrorEvent):void 439 | { 440 | log.error("Error occurred with StageWebView: " + errorEvent); 441 | getAccessTokenEvent.errorCode = "STAGE_WEB_VIEW_ERROR"; 442 | getAccessTokenEvent.errorMessage = "Error occurred with StageWebView"; 443 | dispatchEvent(getAccessTokenEvent); 444 | } // onStageWebViewError 445 | } // getAccessTokenWithImplicitGrant 446 | 447 | /** 448 | * @private 449 | * 450 | * Helper function that completes get-access-token request using the resource owner password credentials grant type. 451 | */ 452 | private function getAccessTokenWithResourceOwnerCredentialsGrant(resourceOwnerCredentialsGrant:ResourceOwnerCredentialsGrant):void 453 | { 454 | // create result event 455 | var getAccessTokenEvent:GetAccessTokenEvent = new GetAccessTokenEvent(); 456 | 457 | // set up URL request 458 | var urlRequest:URLRequest = new URLRequest(tokenEndpoint); 459 | var urlLoader:URLLoader = new URLLoader(); 460 | urlRequest.method = URLRequestMethod.POST; 461 | 462 | // define POST parameters 463 | var urlVariables : URLVariables = new URLVariables(); 464 | urlVariables.grant_type = OAuth2Const.GRANT_TYPE_RESOURCE_OWNER_CREDENTIALS; 465 | urlVariables.client_id = resourceOwnerCredentialsGrant.clientId; 466 | urlVariables.client_secret = resourceOwnerCredentialsGrant.clientSecret; 467 | urlVariables.username = resourceOwnerCredentialsGrant.username; 468 | urlVariables.password = resourceOwnerCredentialsGrant.password; 469 | 470 | // define optional scope parameter only when scope not null 471 | if(resourceOwnerCredentialsGrant.scope !== null) 472 | { 473 | urlVariables.scope = resourceOwnerCredentialsGrant.scope; 474 | } 475 | 476 | urlRequest.data = urlVariables; 477 | 478 | // attach event listeners 479 | urlLoader.addEventListener(Event.COMPLETE, onGetAccessTokenResult); 480 | urlLoader.addEventListener(IOErrorEvent.IO_ERROR, onGetAccessTokenError); 481 | urlLoader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onGetAccessTokenError); 482 | 483 | // make the call 484 | try 485 | { 486 | urlLoader.load(urlRequest); 487 | } // try statement 488 | catch (error:Error) 489 | { 490 | log.error("Error loading token endpoint \"" + tokenEndpoint + "\""); 491 | } // catch statement 492 | 493 | function onGetAccessTokenResult(event:Event):void 494 | { 495 | try 496 | { 497 | var response:Object = com.adobe.serialization.json.JSON.decode(event.target.data); 498 | log.debug("Access token: " + response.access_token); 499 | getAccessTokenEvent.parseAccessTokenResponse(response); 500 | } // try statement 501 | catch (error:JSONParseError) 502 | { 503 | getAccessTokenEvent.errorCode = "com.adobe.serialization.json.JSONParseError"; 504 | getAccessTokenEvent.errorMessage = "Error parsing output from access token response"; 505 | } // catch statement 506 | 507 | dispatchEvent(getAccessTokenEvent); 508 | } // onGetAccessTokenResult 509 | 510 | function onGetAccessTokenError(event:Event):void 511 | { 512 | log.error("Error encountered during access token request: " + event); 513 | 514 | try 515 | { 516 | var error:Object = com.adobe.serialization.json.JSON.decode(event.target.data); 517 | getAccessTokenEvent.errorCode = error.error; 518 | getAccessTokenEvent.errorMessage = error.error_description; 519 | } // try statement 520 | catch (error:JSONParseError) 521 | { 522 | getAccessTokenEvent.errorCode = "Unknown"; 523 | getAccessTokenEvent.errorMessage = "Error encountered during access token request. Unable to parse error message."; 524 | } // catch statement 525 | 526 | dispatchEvent(getAccessTokenEvent); 527 | } // onGetAccessTokenError 528 | } // getAccessTokenWithResourceOwnerCredentialsGrant 529 | 530 | /** 531 | * @private 532 | * 533 | * Helper function to extract query from URL and URL fragment. 534 | */ 535 | private function extractQueryParams(url:String):Object 536 | { 537 | var delimiter:String = (url.indexOf("?") > 0) ? "?" : "#"; 538 | var queryParamsString:String = url.split(delimiter)[1]; 539 | var queryParamsArray:Array = queryParamsString.split("&"); 540 | var queryParams:Object = new Object(); 541 | 542 | for each (var queryParam:String in queryParamsArray) 543 | { 544 | var keyValue:Array = queryParam.split("="); 545 | queryParams[keyValue[0]] = keyValue[1]; 546 | } // for loop 547 | 548 | return queryParams; 549 | } // extractQueryParams 550 | } // class declaration 551 | } // package -------------------------------------------------------------------------------- /src/main/actionscript/com/adobe/protocols/oauth2/OAuth2Const.as: -------------------------------------------------------------------------------- 1 | package com.adobe.protocols.oauth2 2 | { 3 | public class OAuth2Const 4 | { 5 | public static const GRANT_TYPE_AUTHORIZATION_CODE:String = "authorization_code"; 6 | public static const GRANT_TYPE_RESOURCE_OWNER_CREDENTIALS:String = "password"; 7 | public static const GRANT_TYPE_REFRESH_TOKEN:String = "refresh_token"; 8 | 9 | public static const RESPONSE_TYPE_AUTHORIZATION_CODE:String = "code"; 10 | public static const RESPONSE_TYPE_IMPLICIT:String = "token"; 11 | 12 | public static const RESPONSE_PROPERTY_AUTHORIZATION_CODE:String = "code"; 13 | public static const RESPONSE_PROPERTY_ACCESS_TOKEN:String = "access_token"; 14 | 15 | public static const GOOGLE_INSTALLED_APPLICATION_REDIRECT_URI:String = "urn:ietf:wg:oauth:2.0:oob"; 16 | } 17 | } -------------------------------------------------------------------------------- /src/main/actionscript/com/adobe/protocols/oauth2/event/GetAccessTokenEvent.as: -------------------------------------------------------------------------------- 1 | package com.adobe.protocols.oauth2.event 2 | { 3 | import flash.events.Event; 4 | 5 | /** 6 | * Event that is broadcast when results from a getAccessToken 7 | * request are received. 8 | * 9 | * @author Charles Bihis (www.whoischarles.com) 10 | */ 11 | public class GetAccessTokenEvent extends Event implements IOAuth2Event 12 | { 13 | /** 14 | * Event type for this event which encapsulates the response from 15 | * a getAccessToken request. 16 | * 17 | * @eventType getAccessToken 18 | */ 19 | public static const TYPE:String = "getAccessToken"; 20 | 21 | private var _errorCode:String; 22 | private var _errorMessage:String; 23 | private var _accessToken:String; 24 | private var _tokenType:String; 25 | private var _expiresIn:int; 26 | private var _refreshToken:String; 27 | private var _scope:String; 28 | private var _state:String; 29 | private var _response:Object; 30 | 31 | /** 32 | * Constructor. 33 | * 34 | * @param bubbles (Optional) Parameter indicating whether or not the event bubbles 35 | * @param cancelable (Optional Parameter indicating whether or not the event is cancelable 36 | */ 37 | public function GetAccessTokenEvent(bubbles:Boolean = false, cancelable:Boolean = false) 38 | { 39 | super(TYPE, bubbles, cancelable); 40 | } // GetAccessTokenEvent 41 | 42 | /** 43 | * Convenience function that will take a getAccessToken response 44 | * and parse its values. 45 | * 46 | * @param response An object representing the response from a getAccessToken request 47 | */ 48 | public function parseAccessTokenResponse(response:Object):void 49 | { 50 | // required 51 | _accessToken = response.access_token; 52 | _tokenType = response.token_type; 53 | 54 | // optional 55 | _expiresIn = int(response.expires_in); 56 | _refreshToken = response.refresh_token; 57 | _scope = response.scope; 58 | _state = response.state; 59 | 60 | // extra 61 | _response = response; 62 | } 63 | 64 | /** 65 | * Override of the clone function. 66 | * 67 | * @return A new GetAccessTokenEvent object. 68 | */ 69 | public override function clone():Event 70 | { 71 | return new GetAccessTokenEvent(); 72 | } // clone 73 | 74 | /** 75 | * Error code for error after a failed getAccessToken request. 76 | */ 77 | public function get errorCode():String 78 | { 79 | return _errorCode; 80 | } // errorCode 81 | 82 | /** 83 | * @private 84 | */ 85 | public function set errorCode(errorCode:String):void 86 | { 87 | _errorCode = errorCode; 88 | } // errorCode 89 | 90 | /** 91 | * Error message for error after a failed getAccessToken request. 92 | */ 93 | public function get errorMessage():String 94 | { 95 | return _errorMessage; 96 | } // errorMessage 97 | 98 | /** 99 | * @private 100 | */ 101 | public function set errorMessage(errorMessage:String):void 102 | { 103 | _errorMessage = errorMessage; 104 | } // errorMessage 105 | 106 | /** 107 | * The access token issues by the authorization server. 108 | */ 109 | public function get accessToken():String 110 | { 111 | return _accessToken; 112 | } // accessToken 113 | 114 | /** 115 | * The type of the token issued as described in the OAuth 2.0 116 | * v2.15 specification, section 7.1, "Access Token Types". 117 | * 118 | * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-7.1 119 | */ 120 | public function get tokenType():String 121 | { 122 | return _tokenType; 123 | } // tokenType 124 | 125 | /** 126 | * The duration in seconds of the access token lifetime. For example, 127 | * the value "3600" denotes that the access token will expire one hour 128 | * from the time the response was generated. 129 | */ 130 | public function get expiresIn():int 131 | { 132 | return _expiresIn; 133 | } // expiresIn 134 | 135 | /** 136 | * The refresh token which can be used ot obtain new access tokens using 137 | * the same authorization grant as described in the OAuth 2.0 138 | * v2.15 specification, section 6, "Refreshing an Access Token". 139 | * 140 | * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-6 141 | */ 142 | public function get refreshToken():String 143 | { 144 | return _refreshToken; 145 | } // refreshToken 146 | 147 | /** 148 | * The scope of the access request expressed as a list of space-delimited, 149 | * case-sensitive strings. 150 | */ 151 | public function get scope():String 152 | { 153 | return _scope; 154 | } // scope 155 | 156 | /** 157 | * An opaque value used by the client to maintain state between the request 158 | * and callback. 159 | */ 160 | public function get state():String 161 | { 162 | return _state; 163 | } // state 164 | 165 | /** 166 | * Response object to contain all returned response data after a successfull access token request. 167 | */ 168 | public function get response():Object 169 | { 170 | return _response; 171 | } // response 172 | } // class declaration 173 | } // package -------------------------------------------------------------------------------- /src/main/actionscript/com/adobe/protocols/oauth2/event/IOAuth2Event.as: -------------------------------------------------------------------------------- 1 | package com.adobe.protocols.oauth2.event 2 | { 3 | /** 4 | * Interface describing generic OAuth 2.0 events. 5 | * 6 | * @author Charles Bihis (www.whoischarles.com) 7 | */ 8 | public interface IOAuth2Event 9 | { 10 | function get errorCode():String; 11 | function set errorCode(errorCode:String):void; 12 | 13 | function get errorMessage():String; 14 | function set errorMessage(errorMessage:String):void; 15 | } // interface declaration 16 | } // package -------------------------------------------------------------------------------- /src/main/actionscript/com/adobe/protocols/oauth2/event/RefreshAccessTokenEvent.as: -------------------------------------------------------------------------------- 1 | package com.adobe.protocols.oauth2.event 2 | { 3 | import flash.events.Event; 4 | 5 | /** 6 | * Event that is broadcast when results from a refreshAccessToken 7 | * request are received. 8 | * 9 | * @author Charles Bihis (www.whoischarles.com) 10 | */ 11 | public class RefreshAccessTokenEvent extends Event implements IOAuth2Event 12 | { 13 | /** 14 | * Event type for this event which encapsulates the response from 15 | * a refreshAccessToken request. 16 | * 17 | * @eventType refreshAccessToken 18 | */ 19 | public static const TYPE:String = "refreshAccessToken"; 20 | 21 | private var _errorCode:String; 22 | private var _errorMessage:String; 23 | private var _accessToken:String; 24 | private var _tokenType:String; 25 | private var _expiresIn:int; 26 | private var _refreshToken:String; 27 | private var _scope:String; 28 | private var _state:String; 29 | private var _response:Object; 30 | 31 | /** 32 | * Constructor. 33 | * 34 | * @param bubbles (Optional) Parameter indicating whether or not the event bubbles 35 | * @param cancelable (Optional Parameter indicating whether or not the event is cancelable 36 | */ 37 | public function RefreshAccessTokenEvent(bubbles:Boolean = false, cancelable:Boolean = false) 38 | { 39 | super(TYPE, bubbles, cancelable); 40 | } // RefreshAccessTokenEvent 41 | 42 | /** 43 | * Convenience function that will take a refreshAccessToken response 44 | * and parse its values. 45 | * 46 | * @param response An object representing the response from a refreshAccessToken request 47 | */ 48 | public function parseAccessTokenResponse(response:Object):void 49 | { 50 | // required 51 | _accessToken = response.access_token; 52 | _tokenType = response.token_type; 53 | 54 | // optional 55 | _expiresIn = int(response.expires_in); 56 | _refreshToken = response.refresh_token; 57 | _scope = response.scope; 58 | _state = response.state; 59 | 60 | // extra 61 | _response = response; 62 | } 63 | 64 | /** 65 | * Override of the clone function. 66 | * 67 | * @return A new RefreshAccessTokenEvent object. 68 | */ 69 | public override function clone():Event 70 | { 71 | return new GetAccessTokenEvent(); 72 | } // clone 73 | 74 | /** 75 | * Error code for error after a failed refreshAccessToken request. 76 | */ 77 | public function get errorCode():String 78 | { 79 | return _errorCode; 80 | } // errorCode 81 | 82 | /** 83 | * @private 84 | */ 85 | public function set errorCode(errorCode:String):void 86 | { 87 | _errorCode = errorCode; 88 | } // errorCode 89 | 90 | /** 91 | * Error message for error after a failed refreshAccessToken request. 92 | */ 93 | public function get errorMessage():String 94 | { 95 | return _errorMessage; 96 | } // errorMessage 97 | 98 | /** 99 | * @private 100 | */ 101 | public function set errorMessage(errorMessage:String):void 102 | { 103 | _errorMessage = errorMessage; 104 | } // errorMessage 105 | 106 | /** 107 | * The access token issues by the authorization server. 108 | */ 109 | public function get accessToken():String 110 | { 111 | return _accessToken; 112 | } // accessToken 113 | 114 | /** 115 | * The type of the token issued as described in the OAuth 2.0 116 | * v2.15 specification, section 7.1, "Access Token Types". 117 | * 118 | * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-7.1 119 | */ 120 | public function get tokenType():String 121 | { 122 | return _tokenType; 123 | } // tokenType 124 | 125 | /** 126 | * The duration in seconds of the access token lifetime. For example, 127 | * the value "3600" denotes that the access token will expire one hour 128 | * from the time the response was generated. 129 | */ 130 | public function get expiresIn():int 131 | { 132 | return _expiresIn; 133 | } // expiresIn 134 | 135 | /** 136 | * The refresh token which can be used ot obtain new access tokens using 137 | * the same authorization grant as described in the OAuth 2.0 138 | * v2.15 specification, section 6, "Refreshing an Access Token". 139 | * 140 | * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-6 141 | */ 142 | public function get refreshToken():String 143 | { 144 | return _refreshToken; 145 | } // refreshToken 146 | 147 | /** 148 | * The scope of the access request expressed as a list of space-delimited, 149 | * case-sensitive strings. 150 | */ 151 | public function get scope():String 152 | { 153 | return _scope; 154 | } // scope 155 | 156 | /** 157 | * An opaque value used by the client to maintain state between the request 158 | * and callback. 159 | */ 160 | public function get state():String 161 | { 162 | return _state; 163 | } // state 164 | 165 | /** 166 | * Response object to contain all returned response data after a successfull access token request. 167 | */ 168 | public function get response():Object 169 | { 170 | return _response; 171 | } // response 172 | } // class declaration 173 | } // package -------------------------------------------------------------------------------- /src/main/actionscript/com/adobe/protocols/oauth2/grant/AuthorizationCodeGrant.as: -------------------------------------------------------------------------------- 1 | package com.adobe.protocols.oauth2.grant 2 | { 3 | import com.adobe.protocols.oauth2.OAuth2Const; 4 | 5 | import flash.media.StageWebView; 6 | 7 | /** 8 | * Class to encapsulate all of the relevant properties used during 9 | * a get-access-token request using the authorization code grant type. 10 | * 11 | * @author Charles Bihis (www.whoischarles.com) 12 | */ 13 | public class AuthorizationCodeGrant implements IGrantType 14 | { 15 | private var _stageWebView:StageWebView; 16 | private var _clientId:String; 17 | private var _clientSecret:String; 18 | private var _redirectUri:String; 19 | private var _scope:String; 20 | private var _state:Object; 21 | private var _queryParams:Object; 22 | 23 | /** 24 | * Constructor. 25 | * 26 | * @param stageWebView The StageWebView object for which to display the user-consent page 27 | * @param clientId The client identifier 28 | * @param clientSecret The client secret 29 | * @param redirectUri The redirect URI to return to after the authorization process has completed 30 | * @param scope (Optional) The scope of the access request expressed as a list of space-delimited, case-sensitive strings 31 | * @param state (Optional) An opaque value used by the client to maintain state between the request and callback 32 | * @param queryParams (Optional) Additional query parameters that can be passed to the authorization URL 33 | */ 34 | public function AuthorizationCodeGrant(stageWebView:StageWebView, clientId:String, clientSecret:String, redirectUri:String, scope:String = null, state:Object = null, queryParams:Object = null) 35 | { 36 | _stageWebView = stageWebView; 37 | _clientId = clientId; 38 | _clientSecret = clientSecret; 39 | _redirectUri = redirectUri; 40 | _scope = scope; 41 | _state = state; 42 | _queryParams = queryParams; 43 | } // AuthorizationCodeGrant 44 | 45 | /** 46 | * The StageWebView object for which to display the user-consent page. 47 | */ 48 | public function get stageWebView():StageWebView 49 | { 50 | return _stageWebView; 51 | } // stageWebView 52 | 53 | /** 54 | * The client identifier as described in the OAuth spec v2.15, 55 | * section 3, Client Authentication. 56 | * 57 | * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-3 58 | */ 59 | public function get clientId():String 60 | { 61 | return _clientId; 62 | } // clientId 63 | 64 | /** 65 | * The client secret. 66 | */ 67 | public function get clientSecret():String 68 | { 69 | return _clientSecret; 70 | } // clientSecret 71 | 72 | /** 73 | * The redirect endpoint for the client as described in the OAuth 74 | * spec v2.15, section 3.1.2, Redirection Endpoint. 75 | * 76 | * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-3.1.2 77 | */ 78 | public function get redirectUri():String 79 | { 80 | return _redirectUri; 81 | } // redirectUri 82 | 83 | /** 84 | * The scope of the access request expressed as a list of space-delimited, 85 | * case-sensitive strings. 86 | */ 87 | public function get scope():String 88 | { 89 | return _scope; 90 | } // scope 91 | 92 | /** 93 | * An opaque value used by the client to maintain state between the request 94 | * and callback. 95 | */ 96 | public function get state():Object 97 | { 98 | return _state; 99 | } // state 100 | 101 | /** 102 | * Additional query parameters that can be passed to the authorization URL. 103 | */ 104 | public function get queryParams():Object 105 | { 106 | return _queryParams; 107 | } // queryParams 108 | 109 | /** 110 | * Convenience method for getting the full authorization URL. 111 | */ 112 | public function getFullAuthUrl(authEndpoint:String):String 113 | { 114 | var url:String = authEndpoint + "?response_type=" + OAuth2Const.RESPONSE_TYPE_AUTHORIZATION_CODE + "&client_id=" + clientId + "&redirect_uri=" + redirectUri; 115 | 116 | // scope is optional 117 | if (scope != null && scope.length > 0) 118 | { 119 | url += "&scope=" + scope; 120 | } // if statement 121 | 122 | // state is optional 123 | if (state != null) 124 | { 125 | url += "&state=" + state; 126 | } // if statement 127 | 128 | // add additional optional query params, if any 129 | if (queryParams != null) 130 | { 131 | for (var queryParam:String in queryParams) 132 | { 133 | url += "&" + queryParam + "=" + queryParams[queryParam]; 134 | } // for loop 135 | } // if statement 136 | 137 | return url; 138 | } // getFullAuthUrl 139 | } // class declaration 140 | } // package -------------------------------------------------------------------------------- /src/main/actionscript/com/adobe/protocols/oauth2/grant/IGrantType.as: -------------------------------------------------------------------------------- 1 | package com.adobe.protocols.oauth2.grant 2 | { 3 | /** 4 | * Interface used as a marker to signify whether a class 5 | * encapsulates the properties of any of the supported 6 | * OAuth 2.0 grant types, as described by the v2.15 7 | * specification. 8 | * 9 | * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-4 10 | * 11 | * @author Charles Bihis (www.whoischarles.com) 12 | */ 13 | public interface IGrantType 14 | { 15 | function get clientId():String; 16 | } // interface declaration 17 | } // package -------------------------------------------------------------------------------- /src/main/actionscript/com/adobe/protocols/oauth2/grant/ImplicitGrant.as: -------------------------------------------------------------------------------- 1 | package com.adobe.protocols.oauth2.grant 2 | { 3 | import com.adobe.protocols.oauth2.OAuth2Const; 4 | 5 | import flash.media.StageWebView; 6 | 7 | /** 8 | * Class to encapsulate all of the relevant properties used during 9 | * a get-access-token request using the implicit grant type. 10 | * 11 | * @author Charles Bihis (www.whoischarles.com) 12 | */ 13 | public class ImplicitGrant implements IGrantType 14 | { 15 | private var _stageWebView:StageWebView; 16 | private var _clientId:String; 17 | private var _redirectUri:String; 18 | private var _scope:String; 19 | private var _state:Object; 20 | private var _queryParams:Object; 21 | 22 | /** 23 | * Constructor. 24 | * 25 | * @param stageWebView The StageWebView object for which to display the user-consent page 26 | * @param clientId The client identifier 27 | * @param redirectUri The redirect URI to return to after the authorization process has completed 28 | * @param scope (Optional) The scope of the access request expressed as a list of space-delimited, case-sensitive strings 29 | * @param state (Optional) An opaque value used by the client to maintain state between the request and callback 30 | * @param queryParams (Optional) Additional query parameters that can be passed to the authorization URL 31 | */ 32 | public function ImplicitGrant(stageWebView:StageWebView, clientId:String, redirectUri:String, scope:String = null, state:Object = null, queryParams:Object = null) 33 | { 34 | _stageWebView = stageWebView; 35 | _clientId = clientId; 36 | _redirectUri = redirectUri; 37 | _scope = scope; 38 | _state = state; 39 | _queryParams = queryParams; 40 | } // ImplicitGrant 41 | 42 | /** 43 | * The StageWebView object for which to display the user-consent page. 44 | */ 45 | public function get stageWebView():StageWebView 46 | { 47 | return _stageWebView; 48 | } // stageWebView 49 | 50 | /** 51 | * The client identifier as described in the OAuth spec v2.15, 52 | * section 3, Client Authentication. 53 | * 54 | * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-3 55 | */ 56 | public function get clientId():String 57 | { 58 | return _clientId; 59 | } // clientId 60 | 61 | /** 62 | * The redirect endpoint for the client as described in the OAuth 63 | * spec v2.15, section 3.1.2, Redirection Endpoint. 64 | * 65 | * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-3.1.2 66 | */ 67 | public function get redirectUri():String 68 | { 69 | return _redirectUri; 70 | } // redirectUri 71 | 72 | /** 73 | * The scope of the access request expressed as a list of space-delimited, 74 | * case-sensitive strings. 75 | */ 76 | public function get scope():String 77 | { 78 | return _scope; 79 | } // scope 80 | 81 | /** 82 | * An opaque value used by the client to maintain state between the request 83 | * and callback. 84 | */ 85 | public function get state():Object 86 | { 87 | return _state; 88 | } // state 89 | 90 | /** 91 | * Additional query parameters that can be passed to the authorization URL. 92 | */ 93 | public function get queryParams():Object 94 | { 95 | return _queryParams; 96 | } // queryParams 97 | 98 | /** 99 | * Convenience method for getting the full authorization URL. 100 | */ 101 | public function getFullAuthUrl(endpoint:String):String 102 | { 103 | var url:String = endpoint + "?response_type=" + OAuth2Const.RESPONSE_TYPE_IMPLICIT + "&client_id=" + clientId + "&redirect_uri=" + redirectUri; 104 | 105 | // scope is optional 106 | if (scope != null && scope.length > 0) 107 | { 108 | url += "&scope=" + scope; 109 | } // if statement 110 | 111 | // state is optional 112 | if (state != null) 113 | { 114 | url += "&state=" + state; 115 | } // if statement 116 | 117 | // add additional optional query params, if any 118 | if (queryParams != null) 119 | { 120 | for (var queryParam:String in queryParams) 121 | { 122 | url += "&" + queryParam + "=" + queryParams[queryParam]; 123 | } // for loop 124 | } // if statement 125 | 126 | return url; 127 | } // getFullAuthUrl 128 | } // class declaration 129 | } // package -------------------------------------------------------------------------------- /src/main/actionscript/com/adobe/protocols/oauth2/grant/ResourceOwnerCredentialsGrant.as: -------------------------------------------------------------------------------- 1 | package com.adobe.protocols.oauth2.grant 2 | { 3 | /** 4 | * Class to encapsulate all of the relevant properties used during 5 | * a get-access-token request using the resource owner password 6 | * credentials grant type. 7 | * 8 | * @author Charles Bihis (www.hoischarles.com) 9 | */ 10 | public class ResourceOwnerCredentialsGrant implements IGrantType 11 | { 12 | private var _clientId:String; 13 | private var _clientSecret:String; 14 | private var _username:String; 15 | private var _password:String; 16 | private var _scope:String; 17 | 18 | /** 19 | * Constructor. 20 | * 21 | * @param clientId The client identifier 22 | * @param clientSecret The client secret 23 | * @param username The resource owner's username for the authorization server 24 | * @param password The resource owner's password for the authorization server 25 | * @param scope (Optional) The scope of the access request expressed as a list of space-delimited, case-sensitive strings 26 | */ 27 | public function ResourceOwnerCredentialsGrant(clientId:String, clientSecret:String, username:String, password:String, scope:String = null) 28 | { 29 | _clientId = clientId; 30 | _clientSecret = clientSecret; 31 | _username = username; 32 | _password = password; 33 | _scope = scope; 34 | } // ResourceOwnserCredentialsGrant 35 | 36 | /** 37 | * The client identifier as described in the OAuth spec v2.15, 38 | * section 3, Client Authentication. 39 | * 40 | * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-3 41 | */ 42 | public function get clientId():String 43 | { 44 | return _clientId; 45 | } // clientId 46 | 47 | /** 48 | * The client secret. 49 | */ 50 | public function get clientSecret():String 51 | { 52 | return _clientSecret; 53 | } // clientSecret 54 | 55 | /** 56 | * The resource owners username. 57 | */ 58 | public function get username():String 59 | { 60 | return _username; 61 | } // username 62 | 63 | /** 64 | * The resource owner password. 65 | */ 66 | public function get password():String 67 | { 68 | return _password; 69 | } // password 70 | 71 | /** 72 | * The scope of the access request expressed as a list of space-delimited, 73 | * case-sensitive strings. 74 | */ 75 | public function get scope():String 76 | { 77 | return _scope; 78 | } // scope 79 | } // class declaration 80 | } // package -------------------------------------------------------------------------------- /src/test/actionscript/com/adobe/protocols/oauth2/OAuth2Test.as: -------------------------------------------------------------------------------- 1 | package com.adobe.protocols.oauth2 2 | { 3 | public class OAuth2Test 4 | { 5 | [Before] 6 | public function setUp():void 7 | { 8 | } // setUp 9 | 10 | [After] 11 | public function tearDown():void 12 | { 13 | } // tearDown 14 | 15 | [Test(async)] 16 | public function testStub():void 17 | { 18 | } // testStub 19 | } 20 | } --------------------------------------------------------------------------------