├── README.markdown ├── audiencesync └── php │ ├── as-callback.php │ ├── config.php │ └── index.php ├── mobile-sso ├── README.md ├── js-server-demo │ ├── index.js │ ├── package.json │ └── views │ │ └── index.html └── swift-demo │ ├── .DS_Store │ ├── Podfile │ ├── test.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ ├── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ │ └── xcuserdata │ │ │ └── derricklin.xcuserdatad │ │ │ └── UserInterfaceState.xcuserstate │ └── xcuserdata │ │ └── derricklin.xcuserdatad │ │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ │ └── xcschemes │ │ └── xcschememanagement.plist │ ├── test.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── xcuserdata │ │ └── derricklin.xcuserdatad │ │ └── UserInterfaceState.xcuserstate │ └── test │ ├── AppDelegate.swift │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Info.plist │ ├── SceneDelegate.swift │ ├── ViewController.swift │ └── test.xcdatamodeld │ ├── .xccurrentversion │ └── test.xcdatamodel │ └── contents ├── mobile ├── js │ ├── README.md │ └── mobiletemplate.html └── swift-demo-social-login │ ├── README.md │ ├── node_server │ ├── .env │ ├── index.js │ ├── package.json │ └── views │ │ └── index.html │ └── social_login_client │ ├── Podfile │ ├── Podfile.lock │ ├── Pods │ ├── Alamofire │ │ ├── LICENSE │ │ ├── README.md │ │ └── Source │ │ │ ├── AFError.swift │ │ │ ├── Alamofire.swift │ │ │ ├── AlamofireExtended.swift │ │ │ ├── AuthenticationInterceptor.swift │ │ │ ├── CachedResponseHandler.swift │ │ │ ├── Combine.swift │ │ │ ├── DispatchQueue+Alamofire.swift │ │ │ ├── EventMonitor.swift │ │ │ ├── HTTPHeaders.swift │ │ │ ├── HTTPMethod.swift │ │ │ ├── MultipartFormData.swift │ │ │ ├── MultipartUpload.swift │ │ │ ├── NetworkReachabilityManager.swift │ │ │ ├── Notifications.swift │ │ │ ├── OperationQueue+Alamofire.swift │ │ │ ├── ParameterEncoder.swift │ │ │ ├── ParameterEncoding.swift │ │ │ ├── Protected.swift │ │ │ ├── RedirectHandler.swift │ │ │ ├── Request.swift │ │ │ ├── RequestInterceptor.swift │ │ │ ├── RequestTaskMap.swift │ │ │ ├── Response.swift │ │ │ ├── ResponseSerialization.swift │ │ │ ├── Result+Alamofire.swift │ │ │ ├── RetryPolicy.swift │ │ │ ├── ServerTrustEvaluation.swift │ │ │ ├── Session.swift │ │ │ ├── SessionDelegate.swift │ │ │ ├── StringEncoding+Alamofire.swift │ │ │ ├── URLConvertible+URLRequestConvertible.swift │ │ │ ├── URLEncodedFormEncoder.swift │ │ │ ├── URLRequest+Alamofire.swift │ │ │ ├── URLSessionConfiguration+Alamofire.swift │ │ │ └── Validation.swift │ ├── Manifest.lock │ ├── Pods.xcodeproj │ │ └── project.pbxproj │ ├── SwiftyJSON │ │ ├── LICENSE │ │ ├── README.md │ │ └── Source │ │ │ └── SwiftyJSON │ │ │ └── SwiftyJSON.swift │ └── Target Support Files │ │ ├── Alamofire │ │ ├── Alamofire-Info.plist │ │ ├── Alamofire-dummy.m │ │ ├── Alamofire-prefix.pch │ │ ├── Alamofire-umbrella.h │ │ ├── Alamofire.debug.xcconfig │ │ ├── Alamofire.modulemap │ │ └── Alamofire.release.xcconfig │ │ ├── Pods-social_login_client │ │ ├── Pods-social_login_client-Info.plist │ │ ├── Pods-social_login_client-acknowledgements.markdown │ │ ├── Pods-social_login_client-acknowledgements.plist │ │ ├── Pods-social_login_client-dummy.m │ │ ├── Pods-social_login_client-frameworks-Debug-input-files.xcfilelist │ │ ├── Pods-social_login_client-frameworks-Debug-output-files.xcfilelist │ │ ├── Pods-social_login_client-frameworks-Release-input-files.xcfilelist │ │ ├── Pods-social_login_client-frameworks-Release-output-files.xcfilelist │ │ ├── Pods-social_login_client-frameworks.sh │ │ ├── Pods-social_login_client-umbrella.h │ │ ├── Pods-social_login_client.debug.xcconfig │ │ ├── Pods-social_login_client.modulemap │ │ └── Pods-social_login_client.release.xcconfig │ │ └── SwiftyJSON │ │ ├── SwiftyJSON-Info.plist │ │ ├── SwiftyJSON-dummy.m │ │ ├── SwiftyJSON-prefix.pch │ │ ├── SwiftyJSON-umbrella.h │ │ ├── SwiftyJSON.debug.xcconfig │ │ ├── SwiftyJSON.modulemap │ │ └── SwiftyJSON.release.xcconfig │ ├── social_login_client.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ └── social_login_client.xcscheme │ ├── social_login_client.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── social_login_client │ ├── AppDelegate.swift │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Info.plist │ ├── SceneDelegate.swift │ ├── ViewController.swift │ └── test.xcdatamodeld │ ├── .xccurrentversion │ └── test.xcdatamodel │ └── contents ├── notifications ├── README.markdown ├── embed.html └── php │ └── sendnotification.php ├── oauth ├── php │ └── oauth │ │ ├── README.markdown │ │ ├── all-in-one.php │ │ ├── oauth-callback.php │ │ └── oauth-test.php └── python │ ├── oauth_example.py │ └── requirements.txt ├── snippets ├── js │ ├── comment-counts-api │ │ └── commentcounts.html │ └── disqus-reset │ │ ├── README.markdown │ │ └── disqus_reset.html └── php │ ├── batch_add_to_whitelist_from_csv.php │ ├── deduping_script.php │ ├── get-sso-username.php │ ├── get-thread-details.php │ ├── list-100-most-active-users.php │ ├── list-all-replies-to-a-parent.php │ ├── list-all-threads-between-date-and-now.php │ ├── open-threads-since-date.php │ ├── single_access_token.php │ ├── sso_test_recipe.php │ ├── update-thread-title.php │ └── user-details-from-comment-id.php ├── sso ├── coldfusion │ └── sso.cfm ├── cs │ └── DisqusSSO.cs ├── elixir │ └── sso.ex ├── java │ └── sso.java ├── javascript │ ├── hmac-sha1-3.1.2.js │ └── main.js ├── php │ └── sso.php ├── python2 │ └── sso.py ├── python3 │ └── sso.py ├── ruby │ └── sso.rb └── scala │ └── Disqus.scala └── widgets └── php ├── latest_comments.php └── popular_threads ├── config.php ├── getfromcache.php └── getpopularthreads.php /README.markdown: -------------------------------------------------------------------------------- 1 | # DISQUS API Recipes 2 | 3 | A cookbook of common recipes to help expedite your development process when using the [DISQUS API](http://disqus.com/api). 4 | 5 | ## Table of Contents 6 | 7 | ### Beginner 8 | 9 | * Get a thread's details: /php/get-thread-details.php 10 | * List a forum's 100 most active users and their comment counts: /php/list-100-most-active-users.php 11 | * Get an SSO account's username: /php/get-sso-username.php 12 | * Create a guest comment: /php/create-guest-comment.php 13 | * Locates and deletes duplicate comments: /php/deduping_script.php 14 | 15 | ### Intermediate 16 | 17 | * List all replies to a parent comment: /php/list-all-replies-to-a-parent.php 18 | * List all threads created between a given date and now: /php/list-all-threads-between-date-and-now.php 19 | * Add users to the whitelist via CSV file: /php/add_to_whitelist.php 20 | * Close a thread using a single access token: /php/single_access_token.php 21 | * Three-button test script for DISQUS.reset (AJAX reset method): /html/disqus_reset.html 22 | * OAuth example: /oauth/oauth-test.php 23 | 24 | ### Advanced 25 | * Test SSO with a single user: /php/sso_test_recipe.php 26 | * Generate an SSO remote_auth_s3 payload in C# .NET: /ASP.NET/generate_SSO_payload.cs 27 | * (1/2) Get a forum's most popular threads and write to a cache file: /php/get_and_write_popular_threads_to_cache.php 28 | * (2/2) Get popular threads from cache file and display them: /php/get_popular_threads_from_cache.php 29 | 30 | ## Requirements 31 | 32 | For PHP scripts: 33 | 34 | * PHP, preferably the latest version 35 | * (Optional) [DISQUS API bindings for PHP](https://github.com/disqus/disqus-php) — none of these .php files require the bindings. Only use the bindings if you know you need them and already know how to use them. 36 | 37 | ## Usage 38 | 39 | Run any of these files as-is. Enjoy! 40 | 41 | ## Requests 42 | 43 | [Email us any requests](http://disqus.com/support). 44 | 45 | Note: The integrity and utility of these recipes is very important to us. This means keeping them tidy and aimed at a wide audience. If you request a very specific recipe we may make it less specific so it's more accessible. 46 | 47 | ## Support 48 | 49 | These recipes are meant as a starting point. Most recipes have only been tested to work with the latest version of Chrome stable at the time of creation. YMMV. -------------------------------------------------------------------------------- /audiencesync/php/as-callback.php: -------------------------------------------------------------------------------- 1 | 2 | 33 | 34 |

Your website name

35 |

I accept that:

36 | 41 |

Accept

42 | 43 | '; 44 | } 45 | 46 | 47 | // ************************************************************************************** 48 | // 49 | // Get the access token 50 | // 51 | // ************************************************************************************** 52 | 53 | else 54 | { 55 | // Get the code for request access 56 | $code = $_GET['code']; 57 | $audiencesync_uri = urldecode($_GET['audiencesync_uri']); 58 | 59 | // Request the access token 60 | extract($_POST); 61 | 62 | $url = 'https://disqus.com/api/oauth/2.0/access_token/'; 63 | $fields = array( 64 | 'grant_type'=>urlencode("audiencesync"), 65 | 'client_id'=>urlencode(constant("DisqusApiPublic")), 66 | 'client_secret'=>urlencode(constant("DisqusApiSecret")), 67 | 'redirect_uri'=>urlencode(constant("BaseSitePath") . 'as-callback.php?verify=1'), 68 | 'code'=>urlencode($code) 69 | ); 70 | 71 | //url-ify the data for the POST 72 | foreach($fields as $key=>$value) { $fields_string .= $key.'='.$value.'&'; } 73 | rtrim($fields_string, "&"); 74 | 75 | //open connection 76 | $ch = curl_init(); 77 | 78 | //set the url, number of POST vars, POST data 79 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 80 | curl_setopt($ch,CURLOPT_URL,$url); 81 | curl_setopt($ch,CURLOPT_POST,count($fields)); 82 | curl_setopt($ch,CURLOPT_POSTFIELDS,$fields_string); 83 | 84 | //execute post 85 | $result = curl_exec($ch); 86 | 87 | //close connection 88 | curl_close($ch); 89 | 90 | $auth_results = json_decode($result); 91 | 92 | if (isset($auth_results->error)) { 93 | 94 | die($auth_results->error); 95 | 96 | } 97 | 98 | // Extract access token and render 99 | $access_token = $auth_results->access_token; 100 | $user_id = $auth_results->user_id; 101 | $success = $_GET['success']; 102 | 103 | $completion_url = $audiencesync_uri . "?client_id=" . constant("DisqusApiPublic") . "&user_id=" . $user_id . "&access_token=" . $access_token . "&success=" . $success; 104 | 105 | echo ''; 108 | } 109 | ?> -------------------------------------------------------------------------------- /audiencesync/php/config.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /audiencesync/php/index.php: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 |

Audience Sync

10 |

TOS agreement example

11 | 12 |
13 | 29 | 30 | comments powered by Disqus 31 | 32 | -------------------------------------------------------------------------------- /mobile-sso/README.md: -------------------------------------------------------------------------------- 1 | README 2 | ==== 3 | 4 | 5 | ## Requirements 6 | 1. Host a page containing Disqus on an externally-accessible server. (Refer to `views/index.html` for example) 7 | 2. A server that handles SSO data logic on server side. (In the demo, we use the same server to handle SSO data logic and host the page containing Disqus) 8 | 3. Ensure log in via mobile app is secure. The demo has the server pass user unique payload to the mobile app, which is then passed to Disqus via URL. The payload is what authenticates the user. 9 | 4. Have a Pro Disqus site created with SSO feature enabled. 10 | 5. Configure your remote domain at https://disqus.com/api/sso/ 11 | 6. Configure your Disqus application at https://disqus.com/api/applications/ and enter your domain, in this case disqus-sso-demo.glitch.me and select your SSO Domain from the dropdown that you created in step 6. 12 | 13 | In the demo, we mock the serverside logic and the page at https://sleepy-shelf-33354.herokuapp.com/. Feel free to use for your testing purposes. Local development works as well. 14 | 15 | 16 | ## Overview 17 | 18 | The mobile-sso demo includes two parts: 19 | 1. the server-side code (js-server-demo) 20 | a) `https://sleepy-shelf-33354.herokuapp.com/login` is the URL we hit for the SSO payload generation. You will need to integrate authentication here. Refer to `index.js` for more details 21 | b) `views/index.html` will check for the URL parameters `identifier`, `title`, and `payload`. Then it will load it into Disqus config variables. 22 | 2. the client-side/native code in Swift 23 | a) I've included commented out URLs for to hit local host and the server for your convenience. 24 | b) within `login()`, `parameters` is meant to mock the authentication and data retrieval. You will need to implement this. 25 | c) Note: The demo uses Alamofire and SwiftyJSON. 26 | 27 | ### Getting Started 28 | 1. Set up the following server side code to host Disqus. 29 | a) Refer to `index.js` 30 | b) Refer to `views/index.html`. Be sure to edit the other config variables, the public API key, and `s.src` to be `https://your_shortname_goes_here.disqus.com/embed.js` 31 | 2. Authenticate user on mobile app 32 | 3. Make a request to server for payload. This would be the equivalent of hitting https://sleepy-shelf-33354.herokuapp.com/login. 33 | a) Refer to the login function `ViewController.swift` for more details 34 | b) Server side it will hit `app.get("/login" ...` 35 | 4. Make a request to URL pointing to page containing Disqus (requirement 1) with three query parameters. Example URL: ```https://sleepy-shelf-33354.herokuapp.com/?title=Hovsep&identifier=the_hovsep_identifier&payload=payload_goes_here``` 36 | a) SSO payload 37 | b) thread title ([Configuration Variable](https://help.disqus.com/en/articles/1717084-javascript-configuration-variables)) 38 | c) thread identifier ([Configuration Variable](https://help.disqus.com/en/articles/1717084-javascript-configuration-variables)) 39 | 5. Load response in webview. 40 | 41 | ### Technical Flow 42 | 1. User logs into mobile app and needs access to Disqus at a specific thread via SSO. 43 | 2. Mobile app requests a SSO payload from server. ([Generation code](https://github.com/disqus/DISQUS-API-Recipes/tree/master/sso/javascript)) 44 | 3. Server generates and responds with SSO payload. 45 | 4. Mobile App makes a request to URL pointing to page containing Disqus. 46 | 5. Mobile loads response. 47 | ![](https://i.imgur.com/bXS5Q3Y.png) 48 | 49 | 50 | -------------------------------------------------------------------------------- /mobile-sso/js-server-demo/index.js: -------------------------------------------------------------------------------- 1 | // server.js 2 | // where your node app starts 3 | 4 | // init project 5 | if (process.env.NODE_ENV !== 'production') require('dotenv').config() 6 | 7 | var express = require('express'); 8 | var app = express(); 9 | var CryptoJS = require('crypto-js'); 10 | var DISQUS_SECRET = process.env.DISQUS_SECRET; 11 | var DISQUS_PUBLIC = process.env.DISQUS_PUBLIC; 12 | 13 | // http://expressjs.com/en/starter/static-files.html 14 | app.use(express.static('public')); 15 | 16 | // http://expressjs.com/en/starter/basic-routing.html 17 | app.get("/", function (request, response) { 18 | response.sendFile(__dirname + '/views/index.html'); 19 | }); 20 | 21 | // handles login from client and calls SSO code for user in our "database" 22 | app.get("/login", function (request, response) { 23 | console.log("request is", request.query); 24 | let username = request.query.username 25 | let password = request.query.password 26 | let email = request.query.email 27 | if (email === 'youremail_here@disqus.com') { 28 | let payload = disqusSignon(users[0]) 29 | response.send(payload); 30 | } else if (username === 'uniqueperson122327') { 31 | let payload = disqusSignon(users[1]) 32 | response.send(payload); 33 | } 34 | }); 35 | 36 | // Simple in-memory "user database" for the purpose of this demo 37 | var users = [ 38 | // test user 0 39 | { 40 | id:'4226149898', 41 | username:'98989898', 42 | email:'youremail_here@disqus.com', 43 | avatar:'https://i.imgur.com/AYgoB04.jpg', 44 | url:'https://advrider.com/index.php?members/disqustest.422614/', 45 | profile_url:'https://example.com/providedProfileUrl-4226149898' 46 | }, 47 | // test user 1 48 | { 49 | id:'1234523232329', 50 | username: 'uniqueperson122327', 51 | email: 'example@disqus.com', 52 | avatar:'https://i.imgur.com/pTL5Clh.jpg', 53 | url: 'example7.com', 54 | profile_url:'https://example.com/providedProfileUrl-1234523232329' 55 | }, 56 | ]; 57 | 58 | // listen for requests :) 59 | console.log("port is ", process.env.PORT); 60 | app.listen(process.env.PORT, function () { 61 | console.log('Your app is listening on port ' + process.env.PORT); 62 | }); 63 | 64 | // SSO payload generation code from https://github.com/disqus/DISQUS-API-Recipes/tree/master/sso/javascript 65 | 66 | function disqusSignon(user) { 67 | var disqusData = { 68 | id: user.id, 69 | username: user.username, 70 | email: user.email, 71 | // optional 72 | avatar: user.avatar, 73 | url: user.url, 74 | profile_url: user.profile_url 75 | }; 76 | 77 | 78 | // Pass an empty JSON object to generate payload that logs out user with client-side DISQUS.reset() 79 | var disqusNullData = ({}); 80 | 81 | var disqusStr = JSON.stringify(disqusData); 82 | var timestamp = Math.round(+new Date() / 1000); 83 | 84 | /* 85 | * Note that `Buffer` is part of node.js 86 | * For pure Javascript or client-side methods of 87 | * converting to base64, refer to this link: 88 | * http://stackoverflow.com/questions/246801/how-can-you-encode-a-string-to-base64-in-javascript 89 | */ 90 | var message = new Buffer(disqusStr).toString('base64'); 91 | 92 | /* 93 | * CryptoJS is required for hashing (included in dir) 94 | * https://code.google.com/p/crypto-js/ 95 | */ 96 | var result = CryptoJS.HmacSHA1(message + " " + timestamp, DISQUS_SECRET); 97 | var hexsig = CryptoJS.enc.Hex.stringify(result); 98 | 99 | return { 100 | pubKey: DISQUS_PUBLIC, 101 | auth: message + " " + hexsig + " " + timestamp, 102 | }; 103 | } 104 | 105 | 106 | -------------------------------------------------------------------------------- /mobile-sso/js-server-demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mobile-dev", 3 | "version": "1.0.0", 4 | "description": "disqus mobile dev", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/heroku/node-js-getting-started.git" 12 | }, 13 | "author": "", 14 | "license": "ISC", 15 | "bugs": { 16 | "url": "https://github.com/heroku/node-js-getting-started/issues" 17 | }, 18 | "homepage": "https://github.com/heroku/node-js-getting-started#readme", 19 | "dependencies": { 20 | "crypto-js": "^4.0.0", 21 | "express": "^4.17.1" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /mobile-sso/js-server-demo/views/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | Disqus SSO Demo 14 | 15 | 16 | 17 | 18 | 19 | 20 | 23 | 24 | 25 | 26 | 27 | 36 | 37 |
38 | 99 | 100 | 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /mobile-sso/swift-demo/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/disqus/DISQUS-API-Recipes/1b35cc3e2160110a94453a52eff364a1b9f4296b/mobile-sso/swift-demo/.DS_Store -------------------------------------------------------------------------------- /mobile-sso/swift-demo/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment the next line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | target 'test' do 5 | # Comment the next line if you don't want to use dynamic frameworks 6 | use_frameworks! 7 | 8 | # Pods for test 9 | pod “Alamofire” 10 | pod "SwiftyJSON" 11 | 12 | end 13 | -------------------------------------------------------------------------------- /mobile-sso/swift-demo/test.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /mobile-sso/swift-demo/test.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /mobile-sso/swift-demo/test.xcodeproj/project.xcworkspace/xcuserdata/derricklin.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/disqus/DISQUS-API-Recipes/1b35cc3e2160110a94453a52eff364a1b9f4296b/mobile-sso/swift-demo/test.xcodeproj/project.xcworkspace/xcuserdata/derricklin.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /mobile-sso/swift-demo/test.xcodeproj/xcuserdata/derricklin.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /mobile-sso/swift-demo/test.xcodeproj/xcuserdata/derricklin.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | test.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 3 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /mobile-sso/swift-demo/test.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /mobile-sso/swift-demo/test.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /mobile-sso/swift-demo/test.xcworkspace/xcuserdata/derricklin.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/disqus/DISQUS-API-Recipes/1b35cc3e2160110a94453a52eff364a1b9f4296b/mobile-sso/swift-demo/test.xcworkspace/xcuserdata/derricklin.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /mobile-sso/swift-demo/test/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // test 4 | // 5 | // Created by Derrick Lin on 9/1/20. 6 | // Copyright © 2020 Derrick Lin. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CoreData 11 | 12 | @UIApplicationMain 13 | class AppDelegate: UIResponder, UIApplicationDelegate { 14 | 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | // MARK: UISceneSession Lifecycle 23 | 24 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { 25 | // Called when a new scene session is being created. 26 | // Use this method to select a configuration to create the new scene with. 27 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) 28 | } 29 | 30 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { 31 | // Called when the user discards a scene session. 32 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. 33 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return. 34 | } 35 | 36 | // MARK: - Core Data stack 37 | 38 | lazy var persistentContainer: NSPersistentContainer = { 39 | /* 40 | The persistent container for the application. This implementation 41 | creates and returns a container, having loaded the store for the 42 | application to it. This property is optional since there are legitimate 43 | error conditions that could cause the creation of the store to fail. 44 | */ 45 | let container = NSPersistentContainer(name: "test") 46 | container.loadPersistentStores(completionHandler: { (storeDescription, error) in 47 | if let error = error as NSError? { 48 | // Replace this implementation with code to handle the error appropriately. 49 | // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. 50 | 51 | /* 52 | Typical reasons for an error here include: 53 | * The parent directory does not exist, cannot be created, or disallows writing. 54 | * The persistent store is not accessible, due to permissions or data protection when the device is locked. 55 | * The device is out of space. 56 | * The store could not be migrated to the current model version. 57 | Check the error message to determine what the actual problem was. 58 | */ 59 | fatalError("Unresolved error \(error), \(error.userInfo)") 60 | } 61 | }) 62 | return container 63 | }() 64 | 65 | // MARK: - Core Data Saving support 66 | 67 | func saveContext () { 68 | let context = persistentContainer.viewContext 69 | if context.hasChanges { 70 | do { 71 | try context.save() 72 | } catch { 73 | // Replace this implementation with code to handle the error appropriately. 74 | // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. 75 | let nserror = error as NSError 76 | fatalError("Unresolved error \(nserror), \(nserror.userInfo)") 77 | } 78 | } 79 | } 80 | 81 | } 82 | 83 | -------------------------------------------------------------------------------- /mobile-sso/swift-demo/test/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /mobile-sso/swift-demo/test/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /mobile-sso/swift-demo/test/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /mobile-sso/swift-demo/test/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 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 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /mobile-sso/swift-demo/test/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UIApplicationSceneManifest 24 | 25 | UIApplicationSupportsMultipleScenes 26 | 27 | UISceneConfigurations 28 | 29 | UIWindowSceneSessionRoleApplication 30 | 31 | 32 | UISceneConfigurationName 33 | Default Configuration 34 | UISceneDelegateClassName 35 | $(PRODUCT_MODULE_NAME).SceneDelegate 36 | UISceneStoryboardFile 37 | Main 38 | 39 | 40 | 41 | 42 | UILaunchStoryboardName 43 | LaunchScreen 44 | UIMainStoryboardFile 45 | Main 46 | UIRequiredDeviceCapabilities 47 | 48 | armv7 49 | 50 | UISupportedInterfaceOrientations 51 | 52 | UIInterfaceOrientationPortrait 53 | UIInterfaceOrientationLandscapeLeft 54 | UIInterfaceOrientationLandscapeRight 55 | 56 | UISupportedInterfaceOrientations~ipad 57 | 58 | UIInterfaceOrientationPortrait 59 | UIInterfaceOrientationPortraitUpsideDown 60 | UIInterfaceOrientationLandscapeLeft 61 | UIInterfaceOrientationLandscapeRight 62 | 63 | NSAppTransportSecurity 64 | 65 | NSAllowsArbitraryLoads 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /mobile-sso/swift-demo/test/SceneDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.swift 3 | // test 4 | // 5 | // Created by Derrick Lin on 9/1/20. 6 | // Copyright © 2020 Derrick Lin. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class SceneDelegate: UIResponder, UIWindowSceneDelegate { 12 | 13 | var window: UIWindow? 14 | 15 | 16 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { 17 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. 18 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. 19 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). 20 | guard let _ = (scene as? UIWindowScene) else { return } 21 | } 22 | 23 | func sceneDidDisconnect(_ scene: UIScene) { 24 | // Called as the scene is being released by the system. 25 | // This occurs shortly after the scene enters the background, or when its session is discarded. 26 | // Release any resources associated with this scene that can be re-created the next time the scene connects. 27 | // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead). 28 | } 29 | 30 | func sceneDidBecomeActive(_ scene: UIScene) { 31 | // Called when the scene has moved from an inactive state to an active state. 32 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. 33 | } 34 | 35 | func sceneWillResignActive(_ scene: UIScene) { 36 | // Called when the scene will move from an active state to an inactive state. 37 | // This may occur due to temporary interruptions (ex. an incoming phone call). 38 | } 39 | 40 | func sceneWillEnterForeground(_ scene: UIScene) { 41 | // Called as the scene transitions from the background to the foreground. 42 | // Use this method to undo the changes made on entering the background. 43 | } 44 | 45 | func sceneDidEnterBackground(_ scene: UIScene) { 46 | // Called as the scene transitions from the foreground to the background. 47 | // Use this method to save data, release shared resources, and store enough scene-specific state information 48 | // to restore the scene back to its current state. 49 | 50 | // Save changes in the application's managed object context when the application transitions to the background. 51 | (UIApplication.shared.delegate as? AppDelegate)?.saveContext() 52 | } 53 | 54 | 55 | } 56 | 57 | -------------------------------------------------------------------------------- /mobile-sso/swift-demo/test/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // test 4 | // 5 | // Created by Derrick Lin on 9/1/20. 6 | // Copyright © 2020 Derrick Lin. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import WebKit 11 | import Alamofire 12 | import SwiftyJSON 13 | 14 | 15 | class ViewController: UIViewController, WKNavigationDelegate, WKUIDelegate { 16 | 17 | @IBOutlet var mywebview: WKWebView! 18 | 19 | func renderSite(payload: String) { 20 | var components = URLComponents() 21 | components.scheme = "http" 22 | components.host = "localhost" 23 | components.port = 5000 24 | // components.scheme = "https" 25 | // components.host = "sleepy-shelf-33354.herokuapp.com" 26 | // 27 | components.queryItems = [ 28 | URLQueryItem(name: "title", value: "Hovsep"), 29 | URLQueryItem(name: "identifier", value: "the_hovsep_identifier"), 30 | URLQueryItem(name: "payload", value: payload) 31 | ] 32 | let url = components.url! 33 | mywebview.load(URLRequest(url: url)) 34 | } 35 | 36 | func login() { 37 | let parameters = [ 38 | "email": "youremail_here@disqus.com", 39 | "password": "pwd" 40 | ] 41 | // let parameters = [ 42 | // "username": "uniqueperson122327", 43 | // "password": "pwd" 44 | // ] 45 | 46 | // AF.request("https:/sleepy-shelf-33354.herokuapp.com/login", parameters: parameters).responseJSON { response in 47 | AF.request("http://localhost:5000/login", parameters: parameters).responseJSON { response in 48 | switch response.result { 49 | case .success(let value): 50 | let response_dict = (value as! [String: String]) 51 | let auth_payload = response_dict["auth"]!; 52 | self.renderSite(payload: auth_payload) 53 | case .failure(let error): 54 | print(error) 55 | } 56 | } 57 | } 58 | 59 | override func viewDidLoad() { 60 | super.viewDidLoad() 61 | mywebview.navigationDelegate = self 62 | mywebview.uiDelegate = self 63 | login() 64 | } 65 | 66 | 67 | func webView(_ webView: WKWebView, 68 | createWebViewWith configuration: WKWebViewConfiguration, 69 | for navigationAction: WKNavigationAction, 70 | windowFeatures: WKWindowFeatures) -> WKWebView? { 71 | if navigationAction.targetFrame == nil, let url = navigationAction.request.url { 72 | if url.description.lowercased().range(of: "http://") != nil || 73 | url.description.lowercased().range(of: "https://") != nil || 74 | url.description.lowercased().range(of: "mailto:") != nil { 75 | webView.load(navigationAction.request) 76 | } 77 | } 78 | return nil 79 | } 80 | 81 | 82 | func webView( 83 | _ webView: WKWebView, 84 | decidePolicyFor navigationAction: WKNavigationAction, 85 | decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { 86 | 87 | guard let url = navigationAction.request.url else { 88 | decisionHandler(.allow) 89 | return 90 | } 91 | 92 | if (url.absoluteString.contains("/login-success") || 93 | (url.absoluteString.contains("/logout"))) { 94 | decisionHandler(.cancel) 95 | var components = URLComponents() 96 | components.scheme = "http" 97 | components.host = "localhost" 98 | components.port = 5000 99 | // components.scheme = "https" 100 | // components.host = "sleepy-shelf-33354.herokuapp.com" 101 | components.queryItems = [ 102 | URLQueryItem(name: "title", value: "Hovsep"), 103 | URLQueryItem(name: "identifier", value: "the_hovsep_identifier") 104 | ] 105 | mywebview.load(URLRequest(url: components.url!)) 106 | } 107 | else { 108 | decisionHandler(.allow) 109 | } 110 | } 111 | } 112 | 113 | -------------------------------------------------------------------------------- /mobile-sso/swift-demo/test/test.xcdatamodeld/.xccurrentversion: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | _XCCurrentVersionName 6 | test.xcdatamodel 7 | 8 | 9 | -------------------------------------------------------------------------------- /mobile-sso/swift-demo/test/test.xcdatamodeld/test.xcdatamodel/contents: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /mobile/js/README.md: -------------------------------------------------------------------------------- 1 | # Disqus Mobile Recipes 2 | 3 | ## mobiletemplate.html 4 | Example of how you can include Disqus on a single page, for the purpose of embedding in a native app web view. This method uses entirely javascript so it can be hosted on a static cdn, and the required thread parameters are passed in via querystring. -------------------------------------------------------------------------------- /mobile/js/mobiletemplate.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 51 | 52 | blog comments powered by Disqus 53 | 54 | 55 | -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/README.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | The social login demo works in two parts. 3 | 4 | 1. The server-side code that mocks the website hosting Disqus. The relevant code is in the `node_server` folder. It is currently set up to run locally. 5 | 2. The client-side/native code in Swift. Note: The demo uses Alamofire and SwiftyJSON Pods. Be sure to install accordingly. 6 | 7 | # Getting Started 8 | 9 | ## Setting up local server 10 | 1. Set up the following server side code to host Disqus. a) Refer to index.js b) Refer to views/index.html. Be sure to edit the other config variables, the public API key, and s.src to be `https://your_shortname_goes_here.disqus.com/embed.js` 11 | 2. Build the dependencies at `DISQUS-API-Recipes/mobile/swift-demo-social-login/node_server` 12 | 2. Navigate to `DISQUS-API-Recipes/mobile/swift-demo-social-login/node_server` and spin up the local server: 13 | ``` 14 | $ npm install 15 | $ node index.js 16 | 17 | Your app is listening at http://localhost:5000/ 18 | ``` 19 | 3. Verify that the local server is running by going to http://localhost:5000/. 20 | 21 | ## Setting up client environment 22 | 1. Open social_login_client.xcworkspace in Xcode 23 | 2. Build and Run project 24 | 3. At this point you should be able to log in via social auth. 25 | -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/node_server/.env: -------------------------------------------------------------------------------- 1 | PORT=5000 2 | DISQUS_PUBLIC="" 3 | DISQUS_SECRET="" 4 | -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/node_server/index.js: -------------------------------------------------------------------------------- 1 | // server.js 2 | // where your node app starts 3 | 4 | // init project 5 | if (process.env.NODE_ENV !== 'production') require('dotenv').config() 6 | 7 | var express = require('express'); 8 | var app = express(); 9 | var CryptoJS = require('crypto-js'); 10 | var DISQUS_SECRET = process.env.DISQUS_SECRET; 11 | var DISQUS_PUBLIC = process.env.DISQUS_PUBLIC; 12 | 13 | // http://expressjs.com/en/starter/static-files.html 14 | app.use(express.static('public')); 15 | 16 | // http://expressjs.com/en/starter/basic-routing.html 17 | app.get("/", function (request, response) { 18 | response.sendFile(__dirname + '/views/index.html'); 19 | }); 20 | 21 | // listen for requests :) 22 | app.listen(process.env.PORT, function () { 23 | console.log('Your app is listening at http://localhost:' + process.env.PORT + '/'); 24 | }); 25 | 26 | // SSO payload generation code from https://github.com/disqus/DISQUS-API-Recipes/tree/master/sso/javascript 27 | 28 | function disqusSignon(user) { 29 | var disqusData = { 30 | id: user.id, 31 | username: user.username, 32 | email: user.email, 33 | // optional 34 | avatar: user.avatar, 35 | url: user.url, 36 | profile_url: user.profile_url 37 | }; 38 | 39 | 40 | // Pass an empty JSON object to generate payload that logs out user with client-side DISQUS.reset() 41 | var disqusNullData = ({}); 42 | 43 | var disqusStr = JSON.stringify(disqusData); 44 | var timestamp = Math.round(+new Date() / 1000); 45 | 46 | /* 47 | * Note that `Buffer` is part of node.js 48 | * For pure Javascript or client-side methods of 49 | * converting to base64, refer to this link: 50 | * http://stackoverflow.com/questions/246801/how-can-you-encode-a-string-to-base64-in-javascript 51 | */ 52 | var message = new Buffer(disqusStr).toString('base64'); 53 | 54 | /* 55 | * CryptoJS is required for hashing (included in dir) 56 | * https://code.google.com/p/crypto-js/ 57 | */ 58 | var result = CryptoJS.HmacSHA1(message + " " + timestamp, DISQUS_SECRET); 59 | var hexsig = CryptoJS.enc.Hex.stringify(result); 60 | 61 | return { 62 | pubKey: DISQUS_PUBLIC, 63 | auth: message + " " + hexsig + " " + timestamp, 64 | }; 65 | } 66 | 67 | 68 | -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/node_server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mobile-dev", 3 | "version": "1.0.0", 4 | "description": "disqus mobile dev", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "dependencies": { 10 | "crypto-js": "^4.0.0", 11 | "dotenv": "^8.2.0", 12 | "express": "^4.17.1" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/node_server/views/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | Disqus SSO Demo 14 | 15 | 16 | 17 | 18 | 19 | 20 | 23 | 24 | 25 | 26 | 27 | 36 | 37 |
38 | 89 | 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/social_login_client/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment the next line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | target 'social_login_client' do 5 | # Comment the next line if you don't want to use dynamic frameworks 6 | use_frameworks! 7 | 8 | # Pods for test 9 | pod “Alamofire” 10 | pod "SwiftyJSON" 11 | 12 | end 13 | -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/social_login_client/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Alamofire (5.2.2) 3 | - SwiftyJSON (5.0.0) 4 | 5 | DEPENDENCIES: 6 | - Alamofire 7 | - SwiftyJSON 8 | 9 | SPEC REPOS: 10 | trunk: 11 | - Alamofire 12 | - SwiftyJSON 13 | 14 | SPEC CHECKSUMS: 15 | Alamofire: 814429acc853c6c54ff123fc3d2ef66803823ce0 16 | SwiftyJSON: 36413e04c44ee145039d332b4f4e2d3e8d6c4db7 17 | 18 | PODFILE CHECKSUM: b459cb132a6228e86786a356799b57537ff0e92b 19 | 20 | COCOAPODS: 1.9.3 21 | -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/social_login_client/Pods/Alamofire/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014-2020 Alamofire Software Foundation (http://alamofire.org/) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/social_login_client/Pods/Alamofire/Source/Alamofire.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Alamofire.swift 3 | // 4 | // Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | /// Reference to `Session.default` for quick bootstrapping and examples. 26 | public let AF = Session.default 27 | 28 | /// Current Alamofire version. Necessary since SPM doesn't use dynamic libraries. Plus this will be more accurate. 29 | let version = "5.2.2" 30 | -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/social_login_client/Pods/Alamofire/Source/AlamofireExtended.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AlamofireExtended.swift 3 | // 4 | // Copyright (c) 2019 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | /// Type that acts as a generic extension point for all `AlamofireExtended` types. 26 | public struct AlamofireExtension { 27 | /// Stores the type or meta-type of any extended type. 28 | public private(set) var type: ExtendedType 29 | 30 | /// Create an instance from the provided value. 31 | /// 32 | /// - Parameter type: Instance being extended. 33 | public init(_ type: ExtendedType) { 34 | self.type = type 35 | } 36 | } 37 | 38 | /// Protocol describing the `af` extension points for Alamofire extended types. 39 | public protocol AlamofireExtended { 40 | /// Type being extended. 41 | associatedtype ExtendedType 42 | 43 | /// Static Alamofire extension point. 44 | static var af: AlamofireExtension.Type { get set } 45 | /// Instance Alamofire extension point. 46 | var af: AlamofireExtension { get set } 47 | } 48 | 49 | public extension AlamofireExtended { 50 | /// Static Alamofire extension point. 51 | static var af: AlamofireExtension.Type { 52 | get { AlamofireExtension.self } 53 | set {} 54 | } 55 | 56 | /// Instance Alamofire extension point. 57 | var af: AlamofireExtension { 58 | get { AlamofireExtension(self) } 59 | set {} 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/social_login_client/Pods/Alamofire/Source/CachedResponseHandler.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CachedResponseHandler.swift 3 | // 4 | // Copyright (c) 2019 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | /// A type that handles whether the data task should store the HTTP response in the cache. 28 | public protocol CachedResponseHandler { 29 | /// Determines whether the HTTP response should be stored in the cache. 30 | /// 31 | /// The `completion` closure should be passed one of three possible options: 32 | /// 33 | /// 1. The cached response provided by the server (this is the most common use case). 34 | /// 2. A modified version of the cached response (you may want to modify it in some way before caching). 35 | /// 3. A `nil` value to prevent the cached response from being stored in the cache. 36 | /// 37 | /// - Parameters: 38 | /// - task: The data task whose request resulted in the cached response. 39 | /// - response: The cached response to potentially store in the cache. 40 | /// - completion: The closure to execute containing cached response, a modified response, or `nil`. 41 | func dataTask(_ task: URLSessionDataTask, 42 | willCacheResponse response: CachedURLResponse, 43 | completion: @escaping (CachedURLResponse?) -> Void) 44 | } 45 | 46 | // MARK: - 47 | 48 | /// `ResponseCacher` is a convenience `CachedResponseHandler` making it easy to cache, not cache, or modify a cached 49 | /// response. 50 | public struct ResponseCacher { 51 | /// Defines the behavior of the `ResponseCacher` type. 52 | public enum Behavior { 53 | /// Stores the cached response in the cache. 54 | case cache 55 | /// Prevents the cached response from being stored in the cache. 56 | case doNotCache 57 | /// Modifies the cached response before storing it in the cache. 58 | case modify((URLSessionDataTask, CachedURLResponse) -> CachedURLResponse?) 59 | } 60 | 61 | /// Returns a `ResponseCacher` with a follow `Behavior`. 62 | public static let cache = ResponseCacher(behavior: .cache) 63 | /// Returns a `ResponseCacher` with a do not follow `Behavior`. 64 | public static let doNotCache = ResponseCacher(behavior: .doNotCache) 65 | 66 | /// The `Behavior` of the `ResponseCacher`. 67 | public let behavior: Behavior 68 | 69 | /// Creates a `ResponseCacher` instance from the `Behavior`. 70 | /// 71 | /// - Parameter behavior: The `Behavior`. 72 | public init(behavior: Behavior) { 73 | self.behavior = behavior 74 | } 75 | } 76 | 77 | extension ResponseCacher: CachedResponseHandler { 78 | public func dataTask(_ task: URLSessionDataTask, 79 | willCacheResponse response: CachedURLResponse, 80 | completion: @escaping (CachedURLResponse?) -> Void) { 81 | switch behavior { 82 | case .cache: 83 | completion(response) 84 | case .doNotCache: 85 | completion(nil) 86 | case let .modify(closure): 87 | let response = closure(task, response) 88 | completion(response) 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/social_login_client/Pods/Alamofire/Source/DispatchQueue+Alamofire.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DispatchQueue+Alamofire.swift 3 | // 4 | // Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Dispatch 26 | import Foundation 27 | 28 | extension DispatchQueue { 29 | /// Execute the provided closure after a `TimeInterval`. 30 | /// 31 | /// - Parameters: 32 | /// - delay: `TimeInterval` to delay execution. 33 | /// - closure: Closure to execute. 34 | func after(_ delay: TimeInterval, execute closure: @escaping () -> Void) { 35 | asyncAfter(deadline: .now() + delay, execute: closure) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/social_login_client/Pods/Alamofire/Source/HTTPMethod.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HTTPMethod.swift 3 | // 4 | // Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | /// Type representing HTTP methods. Raw `String` value is stored and compared case-sensitively, so 26 | /// `HTTPMethod.get != HTTPMethod(rawValue: "get")`. 27 | /// 28 | /// See https://tools.ietf.org/html/rfc7231#section-4.3 29 | public struct HTTPMethod: RawRepresentable, Equatable, Hashable { 30 | /// `CONNECT` method. 31 | public static let connect = HTTPMethod(rawValue: "CONNECT") 32 | /// `DELETE` method. 33 | public static let delete = HTTPMethod(rawValue: "DELETE") 34 | /// `GET` method. 35 | public static let get = HTTPMethod(rawValue: "GET") 36 | /// `HEAD` method. 37 | public static let head = HTTPMethod(rawValue: "HEAD") 38 | /// `OPTIONS` method. 39 | public static let options = HTTPMethod(rawValue: "OPTIONS") 40 | /// `PATCH` method. 41 | public static let patch = HTTPMethod(rawValue: "PATCH") 42 | /// `POST` method. 43 | public static let post = HTTPMethod(rawValue: "POST") 44 | /// `PUT` method. 45 | public static let put = HTTPMethod(rawValue: "PUT") 46 | /// `TRACE` method. 47 | public static let trace = HTTPMethod(rawValue: "TRACE") 48 | 49 | public let rawValue: String 50 | 51 | public init(rawValue: String) { 52 | self.rawValue = rawValue 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/social_login_client/Pods/Alamofire/Source/MultipartUpload.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MultipartUpload.swift 3 | // 4 | // Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | /// Internal type which encapsulates a `MultipartFormData` upload. 28 | final class MultipartUpload { 29 | lazy var result = Result { try build() } 30 | 31 | let isInBackgroundSession: Bool 32 | let multipartFormData: MultipartFormData 33 | let encodingMemoryThreshold: UInt64 34 | let request: URLRequestConvertible 35 | let fileManager: FileManager 36 | 37 | init(isInBackgroundSession: Bool, 38 | encodingMemoryThreshold: UInt64, 39 | request: URLRequestConvertible, 40 | multipartFormData: MultipartFormData) { 41 | self.isInBackgroundSession = isInBackgroundSession 42 | self.encodingMemoryThreshold = encodingMemoryThreshold 43 | self.request = request 44 | fileManager = multipartFormData.fileManager 45 | self.multipartFormData = multipartFormData 46 | } 47 | 48 | func build() throws -> (request: URLRequest, uploadable: UploadRequest.Uploadable) { 49 | var urlRequest = try request.asURLRequest() 50 | urlRequest.setValue(multipartFormData.contentType, forHTTPHeaderField: "Content-Type") 51 | 52 | let uploadable: UploadRequest.Uploadable 53 | if multipartFormData.contentLength < encodingMemoryThreshold && !isInBackgroundSession { 54 | let data = try multipartFormData.encode() 55 | 56 | uploadable = .data(data) 57 | } else { 58 | let tempDirectoryURL = fileManager.temporaryDirectory 59 | let directoryURL = tempDirectoryURL.appendingPathComponent("org.alamofire.manager/multipart.form.data") 60 | let fileName = UUID().uuidString 61 | let fileURL = directoryURL.appendingPathComponent(fileName) 62 | 63 | try fileManager.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: nil) 64 | 65 | do { 66 | try multipartFormData.writeEncodedData(to: fileURL) 67 | } catch { 68 | // Cleanup after attempted write if it fails. 69 | try? fileManager.removeItem(at: fileURL) 70 | } 71 | 72 | uploadable = .file(fileURL, shouldRemove: true) 73 | } 74 | 75 | return (request: urlRequest, uploadable: uploadable) 76 | } 77 | } 78 | 79 | extension MultipartUpload: UploadConvertible { 80 | func asURLRequest() throws -> URLRequest { 81 | try result.get().request 82 | } 83 | 84 | func createUploadable() throws -> UploadRequest.Uploadable { 85 | try result.get().uploadable 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/social_login_client/Pods/Alamofire/Source/OperationQueue+Alamofire.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OperationQueue+Alamofire.swift 3 | // 4 | // Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | extension OperationQueue { 28 | /// Creates an instance using the provided parameters. 29 | /// 30 | /// - Parameters: 31 | /// - qualityOfService: `QualityOfService` to be applied to the queue. `.default` by default. 32 | /// - maxConcurrentOperationCount: Maximum concurrent operations. 33 | /// `OperationQueue.defaultMaxConcurrentOperationCount` by default. 34 | /// - underlyingQueue: Underlying `DispatchQueue`. `nil` by default. 35 | /// - name: Name for the queue. `nil` by default. 36 | /// - startSuspended: Whether the queue starts suspended. `false` by default. 37 | convenience init(qualityOfService: QualityOfService = .default, 38 | maxConcurrentOperationCount: Int = OperationQueue.defaultMaxConcurrentOperationCount, 39 | underlyingQueue: DispatchQueue? = nil, 40 | name: String? = nil, 41 | startSuspended: Bool = false) { 42 | self.init() 43 | self.qualityOfService = qualityOfService 44 | self.maxConcurrentOperationCount = maxConcurrentOperationCount 45 | self.underlyingQueue = underlyingQueue 46 | self.name = name 47 | isSuspended = startSuspended 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/social_login_client/Pods/Alamofire/Source/RedirectHandler.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RedirectHandler.swift 3 | // 4 | // Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | /// A type that handles how an HTTP redirect response from a remote server should be redirected to the new request. 28 | public protocol RedirectHandler { 29 | /// Determines how the HTTP redirect response should be redirected to the new request. 30 | /// 31 | /// The `completion` closure should be passed one of three possible options: 32 | /// 33 | /// 1. The new request specified by the redirect (this is the most common use case). 34 | /// 2. A modified version of the new request (you may want to route it somewhere else). 35 | /// 3. A `nil` value to deny the redirect request and return the body of the redirect response. 36 | /// 37 | /// - Parameters: 38 | /// - task: The `URLSessionTask` whose request resulted in a redirect. 39 | /// - request: The `URLRequest` to the new location specified by the redirect response. 40 | /// - response: The `HTTPURLResponse` containing the server's response to the original request. 41 | /// - completion: The closure to execute containing the new `URLRequest`, a modified `URLRequest`, or `nil`. 42 | func task(_ task: URLSessionTask, 43 | willBeRedirectedTo request: URLRequest, 44 | for response: HTTPURLResponse, 45 | completion: @escaping (URLRequest?) -> Void) 46 | } 47 | 48 | // MARK: - 49 | 50 | /// `Redirector` is a convenience `RedirectHandler` making it easy to follow, not follow, or modify a redirect. 51 | public struct Redirector { 52 | /// Defines the behavior of the `Redirector` type. 53 | public enum Behavior { 54 | /// Follow the redirect as defined in the response. 55 | case follow 56 | /// Do not follow the redirect defined in the response. 57 | case doNotFollow 58 | /// Modify the redirect request defined in the response. 59 | case modify((URLSessionTask, URLRequest, HTTPURLResponse) -> URLRequest?) 60 | } 61 | 62 | /// Returns a `Redirector` with a `.follow` `Behavior`. 63 | public static let follow = Redirector(behavior: .follow) 64 | /// Returns a `Redirector` with a `.doNotFollow` `Behavior`. 65 | public static let doNotFollow = Redirector(behavior: .doNotFollow) 66 | 67 | /// The `Behavior` of the `Redirector`. 68 | public let behavior: Behavior 69 | 70 | /// Creates a `Redirector` instance from the `Behavior`. 71 | /// 72 | /// - Parameter behavior: The `Behavior`. 73 | public init(behavior: Behavior) { 74 | self.behavior = behavior 75 | } 76 | } 77 | 78 | // MARK: - 79 | 80 | extension Redirector: RedirectHandler { 81 | public func task(_ task: URLSessionTask, 82 | willBeRedirectedTo request: URLRequest, 83 | for response: HTTPURLResponse, 84 | completion: @escaping (URLRequest?) -> Void) { 85 | switch behavior { 86 | case .follow: 87 | completion(request) 88 | case .doNotFollow: 89 | completion(nil) 90 | case let .modify(closure): 91 | let request = closure(task, request, response) 92 | completion(request) 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/social_login_client/Pods/Alamofire/Source/StringEncoding+Alamofire.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StringEncoding+Alamofire.swift 3 | // 4 | // Copyright (c) 2020 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | extension String.Encoding { 28 | /// Creates an encoding from the IANA charset name. 29 | /// 30 | /// - Notes: These mappings match those [provided by CoreFoundation](https://opensource.apple.com/source/CF/CF-476.18/CFStringUtilities.c.auto.html) 31 | /// 32 | /// - Parameter name: IANA charset name. 33 | init?(ianaCharsetName name: String) { 34 | switch name.lowercased() { 35 | case "utf-8": 36 | self = .utf8 37 | case "iso-8859-1": 38 | self = .isoLatin1 39 | case "unicode-1-1", "iso-10646-ucs-2", "utf-16": 40 | self = .utf16 41 | case "utf-16be": 42 | self = .utf16BigEndian 43 | case "utf-16le": 44 | self = .utf16LittleEndian 45 | case "utf-32": 46 | self = .utf32 47 | case "utf-32be": 48 | self = .utf32BigEndian 49 | case "utf-32le": 50 | self = .utf32LittleEndian 51 | default: 52 | return nil 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/social_login_client/Pods/Alamofire/Source/URLConvertible+URLRequestConvertible.swift: -------------------------------------------------------------------------------- 1 | // 2 | // URLConvertible+URLRequestConvertible.swift 3 | // 4 | // Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | /// Types adopting the `URLConvertible` protocol can be used to construct `URL`s, which can then be used to construct 28 | /// `URLRequests`. 29 | public protocol URLConvertible { 30 | /// Returns a `URL` from the conforming instance or throws. 31 | /// 32 | /// - Returns: The `URL` created from the instance. 33 | /// - Throws: Any error thrown while creating the `URL`. 34 | func asURL() throws -> URL 35 | } 36 | 37 | extension String: URLConvertible { 38 | /// Returns a `URL` if `self` can be used to initialize a `URL` instance, otherwise throws. 39 | /// 40 | /// - Returns: The `URL` initialized with `self`. 41 | /// - Throws: An `AFError.invalidURL` instance. 42 | public func asURL() throws -> URL { 43 | guard let url = URL(string: self) else { throw AFError.invalidURL(url: self) } 44 | 45 | return url 46 | } 47 | } 48 | 49 | extension URL: URLConvertible { 50 | /// Returns `self`. 51 | public func asURL() throws -> URL { self } 52 | } 53 | 54 | extension URLComponents: URLConvertible { 55 | /// Returns a `URL` if the `self`'s `url` is not nil, otherwise throws. 56 | /// 57 | /// - Returns: The `URL` from the `url` property. 58 | /// - Throws: An `AFError.invalidURL` instance. 59 | public func asURL() throws -> URL { 60 | guard let url = url else { throw AFError.invalidURL(url: self) } 61 | 62 | return url 63 | } 64 | } 65 | 66 | // MARK: - 67 | 68 | /// Types adopting the `URLRequestConvertible` protocol can be used to safely construct `URLRequest`s. 69 | public protocol URLRequestConvertible { 70 | /// Returns a `URLRequest` or throws if an `Error` was encountered. 71 | /// 72 | /// - Returns: A `URLRequest`. 73 | /// - Throws: Any error thrown while constructing the `URLRequest`. 74 | func asURLRequest() throws -> URLRequest 75 | } 76 | 77 | extension URLRequestConvertible { 78 | /// The `URLRequest` returned by discarding any `Error` encountered. 79 | public var urlRequest: URLRequest? { try? asURLRequest() } 80 | } 81 | 82 | extension URLRequest: URLRequestConvertible { 83 | /// Returns `self`. 84 | public func asURLRequest() throws -> URLRequest { self } 85 | } 86 | 87 | // MARK: - 88 | 89 | extension URLRequest { 90 | /// Creates an instance with the specified `url`, `method`, and `headers`. 91 | /// 92 | /// - Parameters: 93 | /// - url: The `URLConvertible` value. 94 | /// - method: The `HTTPMethod`. 95 | /// - headers: The `HTTPHeaders`, `nil` by default. 96 | /// - Throws: Any error thrown while converting the `URLConvertible` to a `URL`. 97 | public init(url: URLConvertible, method: HTTPMethod, headers: HTTPHeaders? = nil) throws { 98 | let url = try url.asURL() 99 | 100 | self.init(url: url) 101 | 102 | httpMethod = method.rawValue 103 | allHTTPHeaderFields = headers?.dictionary 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/social_login_client/Pods/Alamofire/Source/URLRequest+Alamofire.swift: -------------------------------------------------------------------------------- 1 | // 2 | // URLRequest+Alamofire.swift 3 | // 4 | // Copyright (c) 2019 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | public extension URLRequest { 28 | /// Returns the `httpMethod` as Alamofire's `HTTPMethod` type. 29 | var method: HTTPMethod? { 30 | get { httpMethod.flatMap(HTTPMethod.init) } 31 | set { httpMethod = newValue?.rawValue } 32 | } 33 | 34 | func validate() throws { 35 | if method == .get, let bodyData = httpBody { 36 | throw AFError.urlRequestValidationFailed(reason: .bodyDataInGETRequest(bodyData)) 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/social_login_client/Pods/Alamofire/Source/URLSessionConfiguration+Alamofire.swift: -------------------------------------------------------------------------------- 1 | // 2 | // URLSessionConfiguration+Alamofire.swift 3 | // 4 | // Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | extension URLSessionConfiguration: AlamofireExtended {} 28 | extension AlamofireExtension where ExtendedType: URLSessionConfiguration { 29 | /// Alamofire's default configuration. Same as `URLSessionConfiguration.default` but adds Alamofire default 30 | /// `Accept-Language`, `Accept-Encoding`, and `User-Agent` headers. 31 | public static var `default`: URLSessionConfiguration { 32 | let configuration = URLSessionConfiguration.default 33 | configuration.headers = .default 34 | 35 | return configuration 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/social_login_client/Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Alamofire (5.2.2) 3 | - SwiftyJSON (5.0.0) 4 | 5 | DEPENDENCIES: 6 | - Alamofire 7 | - SwiftyJSON 8 | 9 | SPEC REPOS: 10 | trunk: 11 | - Alamofire 12 | - SwiftyJSON 13 | 14 | SPEC CHECKSUMS: 15 | Alamofire: 814429acc853c6c54ff123fc3d2ef66803823ce0 16 | SwiftyJSON: 36413e04c44ee145039d332b4f4e2d3e8d6c4db7 17 | 18 | PODFILE CHECKSUM: b459cb132a6228e86786a356799b57537ff0e92b 19 | 20 | COCOAPODS: 1.9.3 21 | -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/social_login_client/Pods/SwiftyJSON/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Ruoyu Fu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/social_login_client/Pods/Target Support Files/Alamofire/Alamofire-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 5.2.2 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/social_login_client/Pods/Target Support Files/Alamofire/Alamofire-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Alamofire : NSObject 3 | @end 4 | @implementation PodsDummy_Alamofire 5 | @end 6 | -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/social_login_client/Pods/Target Support Files/Alamofire/Alamofire-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/social_login_client/Pods/Target Support Files/Alamofire/Alamofire-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double AlamofireVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char AlamofireVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/social_login_client/Pods/Target Support Files/Alamofire/Alamofire.debug.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Alamofire 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | OTHER_LDFLAGS = $(inherited) -framework "CFNetwork" 4 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 5 | PODS_BUILD_DIR = ${BUILD_DIR} 6 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_ROOT = ${SRCROOT} 8 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/Alamofire 9 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 10 | SKIP_INSTALL = YES 11 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 12 | -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/social_login_client/Pods/Target Support Files/Alamofire/Alamofire.modulemap: -------------------------------------------------------------------------------- 1 | framework module Alamofire { 2 | umbrella header "Alamofire-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/social_login_client/Pods/Target Support Files/Alamofire/Alamofire.release.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Alamofire 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | OTHER_LDFLAGS = $(inherited) -framework "CFNetwork" 4 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 5 | PODS_BUILD_DIR = ${BUILD_DIR} 6 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_ROOT = ${SRCROOT} 8 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/Alamofire 9 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 10 | SKIP_INSTALL = YES 11 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 12 | -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/social_login_client/Pods/Target Support Files/Pods-social_login_client/Pods-social_login_client-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/social_login_client/Pods/Target Support Files/Pods-social_login_client/Pods-social_login_client-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## Alamofire 5 | 6 | Copyright (c) 2014-2020 Alamofire Software Foundation (http://alamofire.org/) 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | 26 | 27 | ## SwiftyJSON 28 | 29 | The MIT License (MIT) 30 | 31 | Copyright (c) 2017 Ruoyu Fu 32 | 33 | Permission is hereby granted, free of charge, to any person obtaining a copy 34 | of this software and associated documentation files (the "Software"), to deal 35 | in the Software without restriction, including without limitation the rights 36 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 37 | copies of the Software, and to permit persons to whom the Software is 38 | furnished to do so, subject to the following conditions: 39 | 40 | The above copyright notice and this permission notice shall be included in 41 | all copies or substantial portions of the Software. 42 | 43 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 44 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 45 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 46 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 47 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 48 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 49 | THE SOFTWARE. 50 | 51 | Generated by CocoaPods - https://cocoapods.org 52 | -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/social_login_client/Pods/Target Support Files/Pods-social_login_client/Pods-social_login_client-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | Copyright (c) 2014-2020 Alamofire Software Foundation (http://alamofire.org/) 18 | 19 | Permission is hereby granted, free of charge, to any person obtaining a copy 20 | of this software and associated documentation files (the "Software"), to deal 21 | in the Software without restriction, including without limitation the rights 22 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 23 | copies of the Software, and to permit persons to whom the Software is 24 | furnished to do so, subject to the following conditions: 25 | 26 | The above copyright notice and this permission notice shall be included in 27 | all copies or substantial portions of the Software. 28 | 29 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 30 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 31 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 32 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 33 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 34 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 35 | THE SOFTWARE. 36 | 37 | License 38 | MIT 39 | Title 40 | Alamofire 41 | Type 42 | PSGroupSpecifier 43 | 44 | 45 | FooterText 46 | The MIT License (MIT) 47 | 48 | Copyright (c) 2017 Ruoyu Fu 49 | 50 | Permission is hereby granted, free of charge, to any person obtaining a copy 51 | of this software and associated documentation files (the "Software"), to deal 52 | in the Software without restriction, including without limitation the rights 53 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 54 | copies of the Software, and to permit persons to whom the Software is 55 | furnished to do so, subject to the following conditions: 56 | 57 | The above copyright notice and this permission notice shall be included in 58 | all copies or substantial portions of the Software. 59 | 60 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 61 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 62 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 63 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 64 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 65 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 66 | THE SOFTWARE. 67 | 68 | License 69 | MIT 70 | Title 71 | SwiftyJSON 72 | Type 73 | PSGroupSpecifier 74 | 75 | 76 | FooterText 77 | Generated by CocoaPods - https://cocoapods.org 78 | Title 79 | 80 | Type 81 | PSGroupSpecifier 82 | 83 | 84 | StringsTable 85 | Acknowledgements 86 | Title 87 | Acknowledgements 88 | 89 | 90 | -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/social_login_client/Pods/Target Support Files/Pods-social_login_client/Pods-social_login_client-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_social_login_client : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_social_login_client 5 | @end 6 | -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/social_login_client/Pods/Target Support Files/Pods-social_login_client/Pods-social_login_client-frameworks-Debug-input-files.xcfilelist: -------------------------------------------------------------------------------- 1 | ${PODS_ROOT}/Target Support Files/Pods-social_login_client/Pods-social_login_client-frameworks.sh 2 | ${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework 3 | ${BUILT_PRODUCTS_DIR}/SwiftyJSON/SwiftyJSON.framework -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/social_login_client/Pods/Target Support Files/Pods-social_login_client/Pods-social_login_client-frameworks-Debug-output-files.xcfilelist: -------------------------------------------------------------------------------- 1 | ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Alamofire.framework 2 | ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftyJSON.framework -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/social_login_client/Pods/Target Support Files/Pods-social_login_client/Pods-social_login_client-frameworks-Release-input-files.xcfilelist: -------------------------------------------------------------------------------- 1 | ${PODS_ROOT}/Target Support Files/Pods-social_login_client/Pods-social_login_client-frameworks.sh 2 | ${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework 3 | ${BUILT_PRODUCTS_DIR}/SwiftyJSON/SwiftyJSON.framework -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/social_login_client/Pods/Target Support Files/Pods-social_login_client/Pods-social_login_client-frameworks-Release-output-files.xcfilelist: -------------------------------------------------------------------------------- 1 | ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Alamofire.framework 2 | ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftyJSON.framework -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/social_login_client/Pods/Target Support Files/Pods-social_login_client/Pods-social_login_client-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_social_login_clientVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_social_login_clientVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/social_login_client/Pods/Target Support Files/Pods-social_login_client/Pods-social_login_client.debug.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyJSON" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyJSON/SwiftyJSON.framework/Headers" 5 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 6 | OTHER_LDFLAGS = $(inherited) -framework "Alamofire" -framework "CFNetwork" -framework "SwiftyJSON" 7 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 8 | PODS_BUILD_DIR = ${BUILD_DIR} 9 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 10 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 11 | PODS_ROOT = ${SRCROOT}/Pods 12 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 13 | -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/social_login_client/Pods/Target Support Files/Pods-social_login_client/Pods-social_login_client.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_social_login_client { 2 | umbrella header "Pods-social_login_client-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/social_login_client/Pods/Target Support Files/Pods-social_login_client/Pods-social_login_client.release.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyJSON" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyJSON/SwiftyJSON.framework/Headers" 5 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 6 | OTHER_LDFLAGS = $(inherited) -framework "Alamofire" -framework "CFNetwork" -framework "SwiftyJSON" 7 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 8 | PODS_BUILD_DIR = ${BUILD_DIR} 9 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 10 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 11 | PODS_ROOT = ${SRCROOT}/Pods 12 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 13 | -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/social_login_client/Pods/Target Support Files/SwiftyJSON/SwiftyJSON-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 5.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/social_login_client/Pods/Target Support Files/SwiftyJSON/SwiftyJSON-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_SwiftyJSON : NSObject 3 | @end 4 | @implementation PodsDummy_SwiftyJSON 5 | @end 6 | -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/social_login_client/Pods/Target Support Files/SwiftyJSON/SwiftyJSON-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/social_login_client/Pods/Target Support Files/SwiftyJSON/SwiftyJSON-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double SwiftyJSONVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char SwiftyJSONVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/social_login_client/Pods/Target Support Files/SwiftyJSON/SwiftyJSON.debug.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/SwiftyJSON 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 4 | PODS_BUILD_DIR = ${BUILD_DIR} 5 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 6 | PODS_ROOT = ${SRCROOT} 7 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/SwiftyJSON 8 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 9 | SKIP_INSTALL = YES 10 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 11 | -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/social_login_client/Pods/Target Support Files/SwiftyJSON/SwiftyJSON.modulemap: -------------------------------------------------------------------------------- 1 | framework module SwiftyJSON { 2 | umbrella header "SwiftyJSON-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/social_login_client/Pods/Target Support Files/SwiftyJSON/SwiftyJSON.release.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/SwiftyJSON 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 4 | PODS_BUILD_DIR = ${BUILD_DIR} 5 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 6 | PODS_ROOT = ${SRCROOT} 7 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/SwiftyJSON 8 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 9 | SKIP_INSTALL = YES 10 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 11 | -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/social_login_client/social_login_client.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/social_login_client/social_login_client.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/social_login_client/social_login_client.xcodeproj/xcshareddata/xcschemes/social_login_client.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 45 | 51 | 52 | 53 | 54 | 60 | 62 | 68 | 69 | 70 | 71 | 73 | 74 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/social_login_client/social_login_client.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/social_login_client/social_login_client.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/social_login_client/social_login_client/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // test 4 | // 5 | // Created by Derrick Lin on 9/1/20. 6 | // Copyright © 2020 Derrick Lin. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CoreData 11 | 12 | @UIApplicationMain 13 | class AppDelegate: UIResponder, UIApplicationDelegate { 14 | 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | // MARK: UISceneSession Lifecycle 23 | 24 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { 25 | // Called when a new scene session is being created. 26 | // Use this method to select a configuration to create the new scene with. 27 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) 28 | } 29 | 30 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { 31 | // Called when the user discards a scene session. 32 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. 33 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return. 34 | } 35 | 36 | // MARK: - Core Data stack 37 | 38 | lazy var persistentContainer: NSPersistentContainer = { 39 | /* 40 | The persistent container for the application. This implementation 41 | creates and returns a container, having loaded the store for the 42 | application to it. This property is optional since there are legitimate 43 | error conditions that could cause the creation of the store to fail. 44 | */ 45 | let container = NSPersistentContainer(name: "test") 46 | container.loadPersistentStores(completionHandler: { (storeDescription, error) in 47 | if let error = error as NSError? { 48 | // Replace this implementation with code to handle the error appropriately. 49 | // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. 50 | 51 | /* 52 | Typical reasons for an error here include: 53 | * The parent directory does not exist, cannot be created, or disallows writing. 54 | * The persistent store is not accessible, due to permissions or data protection when the device is locked. 55 | * The device is out of space. 56 | * The store could not be migrated to the current model version. 57 | Check the error message to determine what the actual problem was. 58 | */ 59 | fatalError("Unresolved error \(error), \(error.userInfo)") 60 | } 61 | }) 62 | return container 63 | }() 64 | 65 | // MARK: - Core Data Saving support 66 | 67 | func saveContext () { 68 | let context = persistentContainer.viewContext 69 | if context.hasChanges { 70 | do { 71 | try context.save() 72 | } catch { 73 | // Replace this implementation with code to handle the error appropriately. 74 | // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. 75 | let nserror = error as NSError 76 | fatalError("Unresolved error \(nserror), \(nserror.userInfo)") 77 | } 78 | } 79 | } 80 | 81 | } 82 | 83 | -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/social_login_client/social_login_client/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/social_login_client/social_login_client/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/social_login_client/social_login_client/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/social_login_client/social_login_client/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 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 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/social_login_client/social_login_client/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UIApplicationSceneManifest 24 | 25 | UIApplicationSupportsMultipleScenes 26 | 27 | UISceneConfigurations 28 | 29 | UIWindowSceneSessionRoleApplication 30 | 31 | 32 | UISceneConfigurationName 33 | Default Configuration 34 | UISceneDelegateClassName 35 | $(PRODUCT_MODULE_NAME).SceneDelegate 36 | UISceneStoryboardFile 37 | Main 38 | 39 | 40 | 41 | 42 | UILaunchStoryboardName 43 | LaunchScreen 44 | UIMainStoryboardFile 45 | Main 46 | UIRequiredDeviceCapabilities 47 | 48 | armv7 49 | 50 | UISupportedInterfaceOrientations 51 | 52 | UIInterfaceOrientationPortrait 53 | UIInterfaceOrientationLandscapeLeft 54 | UIInterfaceOrientationLandscapeRight 55 | 56 | UISupportedInterfaceOrientations~ipad 57 | 58 | UIInterfaceOrientationPortrait 59 | UIInterfaceOrientationPortraitUpsideDown 60 | UIInterfaceOrientationLandscapeLeft 61 | UIInterfaceOrientationLandscapeRight 62 | 63 | NSAppTransportSecurity 64 | 65 | NSAllowsArbitraryLoads 66 | 67 | 68 | NSCrossWebsiteTrackingUsageDescription 69 | Allow authentication and device trackers. 70 | 71 | 72 | -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/social_login_client/social_login_client/SceneDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.swift 3 | // test 4 | // 5 | // Created by Derrick Lin on 9/1/20. 6 | // Copyright © 2020 Derrick Lin. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class SceneDelegate: UIResponder, UIWindowSceneDelegate { 12 | 13 | var window: UIWindow? 14 | 15 | 16 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { 17 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. 18 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. 19 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). 20 | guard let _ = (scene as? UIWindowScene) else { return } 21 | } 22 | 23 | func sceneDidDisconnect(_ scene: UIScene) { 24 | // Called as the scene is being released by the system. 25 | // This occurs shortly after the scene enters the background, or when its session is discarded. 26 | // Release any resources associated with this scene that can be re-created the next time the scene connects. 27 | // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead). 28 | } 29 | 30 | func sceneDidBecomeActive(_ scene: UIScene) { 31 | // Called when the scene has moved from an inactive state to an active state. 32 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. 33 | } 34 | 35 | func sceneWillResignActive(_ scene: UIScene) { 36 | // Called when the scene will move from an active state to an inactive state. 37 | // This may occur due to temporary interruptions (ex. an incoming phone call). 38 | } 39 | 40 | func sceneWillEnterForeground(_ scene: UIScene) { 41 | // Called as the scene transitions from the background to the foreground. 42 | // Use this method to undo the changes made on entering the background. 43 | } 44 | 45 | func sceneDidEnterBackground(_ scene: UIScene) { 46 | // Called as the scene transitions from the foreground to the background. 47 | // Use this method to save data, release shared resources, and store enough scene-specific state information 48 | // to restore the scene back to its current state. 49 | 50 | // Save changes in the application's managed object context when the application transitions to the background. 51 | (UIApplication.shared.delegate as? AppDelegate)?.saveContext() 52 | } 53 | 54 | 55 | } 56 | 57 | -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/social_login_client/social_login_client/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // test 4 | // 5 | // Created by Derrick Lin on 9/1/20. 6 | // Copyright © 2020 Derrick Lin. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import WebKit 11 | import Alamofire 12 | import SwiftyJSON 13 | 14 | 15 | class ViewController: UIViewController, WKNavigationDelegate, WKUIDelegate { 16 | 17 | @IBOutlet var mywebview: WKWebView! 18 | 19 | func renderSite() { 20 | var components = URLComponents() 21 | components.scheme = "http" 22 | components.host = "localhost" 23 | components.port = 5000 24 | 25 | components.queryItems = [ 26 | URLQueryItem(name: "title", value: "Hovsep"), 27 | URLQueryItem(name: "identifier", value: "the_hovsep_identifier"), 28 | ] 29 | let url = components.url! 30 | mywebview.load(URLRequest(url: url)) 31 | } 32 | 33 | override func viewDidLoad() { 34 | super.viewDidLoad() 35 | mywebview.navigationDelegate = self 36 | mywebview.uiDelegate = self 37 | renderSite() 38 | } 39 | 40 | override func loadView() { 41 | let webConfiguration = WKWebViewConfiguration() 42 | webConfiguration.applicationNameForUserAgent = "Mozilla/5.0 (iPhone; CPU iPhone OS 13_5_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.1 Mobile/15E148 Safari/604.1" 43 | mywebview = WKWebView(frame: .zero, configuration: webConfiguration) 44 | mywebview.uiDelegate = self 45 | view = mywebview 46 | } 47 | 48 | func webView(_ webView: WKWebView, 49 | createWebViewWith configuration: WKWebViewConfiguration, 50 | for navigationAction: WKNavigationAction, 51 | windowFeatures: WKWindowFeatures) -> WKWebView? { 52 | if navigationAction.targetFrame == nil, let url = navigationAction.request.url { 53 | if url.description.lowercased().range(of: "http://") != nil || 54 | url.description.lowercased().range(of: "https://") != nil || 55 | url.description.lowercased().range(of: "mailto:") != nil { 56 | webView.load(navigationAction.request) 57 | } 58 | } 59 | return nil 60 | } 61 | 62 | 63 | func webView( 64 | _ webView: WKWebView, 65 | decidePolicyFor navigationAction: WKNavigationAction, 66 | decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { 67 | 68 | guard let url = navigationAction.request.url else { 69 | decisionHandler(.allow) 70 | return 71 | } 72 | 73 | if ( 74 | url.absoluteString.contains("/login-success") 75 | || (url.absoluteString.contains("example.com/logout")) 76 | ) { 77 | decisionHandler(.cancel) 78 | var components = URLComponents() 79 | components.scheme = "http" 80 | components.host = "localhost" 81 | components.port = 5000 82 | components.queryItems = [ 83 | URLQueryItem(name: "title", value: "Hovsep"), 84 | URLQueryItem(name: "identifier", value: "the_hovsep_identifier") 85 | ] 86 | mywebview.load(URLRequest(url: components.url!)) 87 | } 88 | else { 89 | decisionHandler(.allow) 90 | } 91 | } 92 | } 93 | 94 | -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/social_login_client/social_login_client/test.xcdatamodeld/.xccurrentversion: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /mobile/swift-demo-social-login/social_login_client/social_login_client/test.xcdatamodeld/test.xcdatamodel/contents: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /notifications/README.markdown: -------------------------------------------------------------------------------- 1 | # Sending custom email notifications 2 | 3 | This example shows you how to send custom email notifications whenever a new comment is posted on a thread. The most common use will be to send the post author an email notification when a new comment is posted in their article. 4 | 5 | This example is written in PHP, but can be adapted to any language. 6 | 7 | ## Before you begin 8 | 9 | * Make sure you've registered a [Disqus API application](http://disqus.com/api/applications/) 10 | * Basic API accounts are limited to 1000 requests an hour, which should be fine unless you get more than 1000 comments an hour. 11 | * This example requires modification so you can find the proper author email addresses given a post ID, URL, or any other data you choose. 12 | 13 | ## Files 14 | 15 | ### embed.html 16 | 17 | A sample of the Disqus comments embed with the proper onNewComment callback. When a new comment is posted, it'll POST data to sendnotification.php which sends the actual email notification. 18 | 19 | ### php/sendnotification.php 20 | 21 | This script receives data from the embed callback, makes a Disqus API request, and sends a formatted email to the author. 22 | 23 | ## Support 24 | 25 | This recipe is a starting point, and actual usage must be adapted to your own system and needs. No support is offered for this recipe. -------------------------------------------------------------------------------- /notifications/embed.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 45 | 46 | comments powered by Disqus 47 | -------------------------------------------------------------------------------- /notifications/php/sendnotification.php: -------------------------------------------------------------------------------- 1 | response->author; 49 | 50 | $thread = $results->response->thread; 51 | 52 | $comment = $results->response->raw_message; 53 | 54 | 55 | // Build the email message 56 | 57 | $headers = 'MIME-Version: 1.0' . "\r\n"; 58 | $headers .= 'Content-type: text/html; charset=iso-8859-1' . "\r\n"; 59 | $headers .= 'From:someone-auto@example.com' . '\r\n'; // TODO replace with your own notifier email 60 | 61 | $subject = 'New comment on ' . $thread->title; 62 | 63 | $message = '

A comment was posted on ' . $thread->title . '

' . $author->name . ' wrote:

' . $comment .'

Moderate comment

'; 64 | 65 | 66 | 67 | // Send the email 68 | mail($postAuthor,$subject,$message,$headers); 69 | 70 | echo 'Sent email to ' . $postAuthor; 71 | ?> -------------------------------------------------------------------------------- /oauth/php/oauth/README.markdown: -------------------------------------------------------------------------------- 1 | # DISQUS OAuth Test 2 | 3 | This recipe will show you a method of setting up a simple OAuth session and recieving the Access Token necessary to make API requests requiring authentication. 4 | 5 | How it Works: 6 | * oauth-test.php provides a link which will pass the string necessary to pull up an authentication page within DISQUS 7 | * After authenticating you'll be sent to your redirect URL (example.com/oauth-callback.php) 8 | * You'll then be provided with the access token necessary to use API methods that require authentication 9 | 10 | Additional information on DISQUS authentication can be found here: http://disqus.com/api/docs/auth/ -------------------------------------------------------------------------------- /oauth/php/oauth/all-in-one.php: -------------------------------------------------------------------------------- 1 | "; 9 | $SECRET_KEY = ""; 10 | $redirect = "http:///all-in-one.php"; 11 | 12 | $endpoint = 'https://disqus.com/api/oauth/2.0/authorize?'; 13 | $client_id = $PUBLIC_KEY; 14 | $scope = 'read,write'; 15 | $response_type = 'code'; 16 | 17 | $auth_url = $endpoint.'&client_id='.$client_id.'&scope='.$scope.'&response_type='.$response_type.'&redirect_uri='.$redirect; 18 | 19 | echo $auth_url; 20 | 21 | // Trigger the initial authentication call to receive a code 22 | echo "

Trigger authentication -> OAuth

"; 23 | 24 | 25 | // Get the code to request access 26 | $CODE = $_GET['code']; 27 | 28 | if($CODE){ 29 | 30 | // Build the URL and request the authentication token 31 | extract($_POST); 32 | 33 | $authorize = "authorization_code"; 34 | 35 | $url = 'https://disqus.com/api/oauth/2.0/access_token/?'; 36 | $fields = array( 37 | 'grant_type'=>urlencode($authorize), 38 | 'client_id'=>urlencode($PUBLIC_KEY), 39 | 'client_secret'=>urlencode($SECRET_KEY), 40 | 'redirect_uri'=>urlencode($redirect), 41 | 'code'=>urlencode($CODE) 42 | ); 43 | 44 | //url-ify the data for the POST 45 | foreach($fields as $key=>$value) { $fields_string .= $key.'='.$value.'&'; } 46 | rtrim($fields_string, "&"); 47 | 48 | //open connection 49 | $ch = curl_init(); 50 | 51 | //set the url, number of POST vars, POST data 52 | curl_setopt($ch,CURLOPT_URL,$url); 53 | curl_setopt($ch,CURLOPT_POST,count($fields)); 54 | curl_setopt($ch,CURLOPT_POSTFIELDS,$fields_string); 55 | curl_setopt($ch,CURLOPT_RETURNTRANSFER,1); 56 | 57 | //execute post 58 | $data = curl_exec($ch); 59 | 60 | //close connection 61 | curl_close($ch); 62 | 63 | //turn the string into a object 64 | $auth_results = json_decode($data); 65 | 66 | 67 | echo "

The authentication information returned:

"; 68 | var_dump($auth_results); 69 | echo "

"; 70 | 71 | $access_token = $auth_results->access_token; 72 | 73 | echo "

The access token you'll use in API calls:

"; 74 | echo $access_token; 75 | echo "

"; 76 | echo $auth_results->access_token; 77 | 78 | 79 | function getData($url, $SECRET_KEY, $access_token){ 80 | 81 | //Setting OAuth parameters 82 | $oauth_params = (object) array( 83 | 'access_token' => $access_token, 84 | 'api_secret' => $SECRET_KEY 85 | ); 86 | 87 | $param_string = ''; 88 | 89 | 90 | //Build the endpiont from the fields selected and put add it to the string. 91 | 92 | //foreach($params as $key=>$value) { $param_string .= $key.'='.$value.'&'; } 93 | foreach($oauth_params as $key=>$value) { $param_string .= $key.'='.$value.'&'; } 94 | $param_string = rtrim($param_string, "&"); 95 | 96 | // setup curl to make a call to the endpoint 97 | $url .= $param_string; 98 | 99 | //echo $url; 100 | $session = curl_init($url); 101 | 102 | // indicates that we want the response back rather than just returning a "TRUE" string 103 | curl_setopt($session, CURLOPT_RETURNTRANSFER, true); 104 | curl_setopt($session,CURLOPT_FOLLOWLOCATION,true); 105 | 106 | // execute GET and get the session backs 107 | $results = curl_exec($session); 108 | // close connection 109 | curl_close($session); 110 | // show the response in the browser 111 | return json_decode($results); 112 | } 113 | 114 | 115 | //Setting the correct endpoint 116 | $cases_endpoint = 'https://disqus.com/api/3.0/users/details.json?'; 117 | 118 | //Calling the function to getData 119 | $user_details = getData($cases_endpoint, $SECRET_KEY, $access_token); 120 | echo "

Getting user details:

"; 121 | var_dump($user_details); 122 | echo "

"; 123 | 124 | //Setting the correct endpoint 125 | $forums_endpoint = 'https://disqus.com/api/3.0/users/listForums.json?'; 126 | 127 | //Calling the function to getData 128 | $forum_details = getData($forums_endpoint, $SECRET_KEY, $access_token); 129 | echo "

Getting forum details:

"; 130 | var_dump($forum_details); 131 | echo "

"; 132 | } 133 | 134 | ?> 135 | 136 | 137 | 138 | 139 | -------------------------------------------------------------------------------- /oauth/php/oauth/oauth-callback.php: -------------------------------------------------------------------------------- 1 | urlencode($authorize), 18 | 'client_id'=>urlencode($PUBLIC_KEY), 19 | 'client_secret'=>urlencode($SECRET_KEY), 20 | 'redirect_uri'=>urlencode($redirect), 21 | 'code'=>urlencode($CODE) 22 | ); 23 | 24 | //url-ify the data for the POST 25 | foreach($fields as $key=>$value) { $fields_string .= $key.'='.$value.'&'; } 26 | rtrim($fields_string, "&"); 27 | 28 | //open connection 29 | $ch = curl_init(); 30 | 31 | //set the url, number of POST vars, POST data 32 | curl_setopt($ch,CURLOPT_URL,$url); 33 | curl_setopt($ch,CURLOPT_POST,count($fields)); 34 | curl_setopt($ch,CURLOPT_POSTFIELDS,$fields_string); 35 | 36 | //execute post 37 | $result = curl_exec($ch); 38 | 39 | //close connection 40 | curl_close($ch); 41 | 42 | ?> 43 | -------------------------------------------------------------------------------- /oauth/php/oauth/oauth-test.php: -------------------------------------------------------------------------------- 1 | OAuth"; 5 | 6 | ?> 7 | 8 | 9 | -------------------------------------------------------------------------------- /oauth/python/oauth_example.py: -------------------------------------------------------------------------------- 1 | # author adam@disqus.com 2 | 3 | from flask import Flask 4 | app = Flask(__name__) 5 | 6 | from flask import session 7 | from flask import redirect 8 | from flask import url_for 9 | from flask import escape 10 | from flask import request 11 | 12 | import functools 13 | import simplejson 14 | import urllib 15 | import urllib2 16 | 17 | from disqusapi import InvalidAccessToken 18 | from disqusapi import DisqusAPI 19 | 20 | ## TODO: make this read from a file 21 | public_key = "PUT YOUR KEY HERE" 22 | secret_key = "PUT YOUR KEY HERE" 23 | 24 | app.secret_key = "this is my salt" 25 | 26 | disqus = DisqusAPI(secret_key, public_key) 27 | 28 | 29 | ################################################################################ 30 | ## UTILS 31 | 32 | 33 | class User(object): 34 | """User object based on disqus auth token""" 35 | def __init__(self, username, user_id, access_token, expires_in, token_type, refresh_token): 36 | super(User, self).__init__() 37 | self.username = username 38 | self.user_id = user_id 39 | self.access_token = access_token 40 | self.expires_in = expires_in 41 | self.token_type = token_type 42 | self.refresh_token = refresh_token 43 | 44 | def __repr__(self): 45 | return "<{username}:{access_token}>".format(**self.__dict__) 46 | 47 | 48 | def current_user(): 49 | cu = None 50 | if 'auth' in session: 51 | auth = session['auth'] 52 | cu = User(**auth) 53 | return cu 54 | 55 | 56 | ################################################################################ 57 | ## APP 58 | 59 | 60 | @app.route("/") 61 | def hello(): 62 | cu = current_user() 63 | if cu: 64 | return 'Logged in as %s' % escape(session['username']) 65 | else: 66 | return redirect('/oauth/authorize/') 67 | 68 | 69 | @app.route("/bye") 70 | def goodbye(): 71 | return "Goodbye World!" 72 | 73 | 74 | @app.route("/foo//") 75 | def foobarbaz(bar, baz): 76 | if bar == 1: 77 | return "yay it's a one!" 78 | elif bar == 2: 79 | return "two" 80 | else: 81 | return baz 82 | 83 | 84 | ################################################################################ 85 | ## AUTH STUFF (DO NOT CHANGE) 86 | 87 | class Logout(Exception): 88 | pass 89 | 90 | 91 | def api_call(func, **kwargs): 92 | try: 93 | if 'auth' in session: 94 | result = func(access_token=session['auth']['access_token'], **kwargs) 95 | else: 96 | result = func(**kwargs) 97 | except InvalidAccessToken: 98 | raise Logout 99 | return result 100 | 101 | 102 | @app.errorhandler(Logout) 103 | def logout_handler(error): 104 | try: 105 | del session['auth'] 106 | except KeyError: 107 | pass 108 | return redirect(url_for('oauth_authorize')) 109 | 110 | 111 | def login_required(func): 112 | @functools.wraps(func) 113 | def wrapped(*args, **kwargs): 114 | if 'auth' not in session: 115 | return redirect(url_for('oauth_authorize')) 116 | return func(*args, **kwargs) 117 | return wrapped 118 | 119 | 120 | @app.route('/oauth/authorize/') 121 | def oauth_authorize(): 122 | return redirect('https://disqus.com/api/oauth/2.0/authorize/?%s' % (urllib.urlencode({ 123 | 'client_id': disqus.public_key, 124 | 'scope': 'read,write', 125 | 'response_type': 'code', 126 | 'redirect_uri': url_for('oauth_callback', _external=True), 127 | }),)) 128 | 129 | 130 | @app.route('/oauth/callback/') 131 | def oauth_callback(): 132 | code = request.args.get('code') 133 | error = request.args.get('error') 134 | if error or not code: 135 | # TODO: show error 136 | return redirect('/') 137 | 138 | req = urllib2.Request('https://disqus.com/api/oauth/2.0/access_token/', urllib.urlencode({ 139 | 'grant_type': 'authorization_code', 140 | 'client_id': disqus.public_key, 141 | 'client_secret': disqus.secret_key, 142 | 'redirect_uri': url_for('oauth_callback', _external=True), 143 | 'code': code, 144 | })) 145 | 146 | resp = urllib2.urlopen(req).read() 147 | 148 | data = simplejson.loads(resp) 149 | 150 | session['auth'] = data 151 | session['username'] = data['username'] 152 | session.permanent = True 153 | 154 | return redirect('/') 155 | 156 | ################################################################################ 157 | ## RUN IT 158 | 159 | if __name__ == "__main__": 160 | app.config["DEBUG"] = True 161 | app.run() 162 | -------------------------------------------------------------------------------- /oauth/python/requirements.txt: -------------------------------------------------------------------------------- 1 | flask 2 | simplejson 3 | disqusapi 4 | -------------------------------------------------------------------------------- /snippets/js/comment-counts-api/commentcounts.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Disqus Comment Counts Example 5 | 6 | 7 | 12 | 13 | 14 |

Comment Counts Example

15 | 21 | 27 | 33 | 34 | 35 | 36 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /snippets/js/disqus-reset/README.markdown: -------------------------------------------------------------------------------- 1 | # Using Disqus on AJAX/Single page applications 2 | 3 | This is an example of how to use DISQUS.reset to change the identifier, URL, title and language of a thread without reloading the page. 4 | 5 | This function won't circumvent the need for each unique discussion to have a unique URL and identifier. For more information see: [Why are the same comments showing up on multiple pages?](http://help.disqus.com/customer/portal/articles/662547) 6 | 7 | ## Prerequisites 8 | 9 | * Make sure you've registered a [Disqus site](http://disqus.com/admin/create/) (also known as a shortname) 10 | 11 | ## Screenshot 12 | 13 | ![DISQUS.reset screenshot](https://photos-4.dropbox.com/t/0/AAAnxjsQ41qUfFluaQVa07BKnIRg4Wm84TxHuEwcB3Umkw/12/13552259/png/1024x768/3/1389657600/0/2/shot_140113_143204.png/UpOSQ9c7XlmkxlxfwdtQvA1WbndUbNCW7Lo5VjfwHhs) 14 | -------------------------------------------------------------------------------- /snippets/js/disqus-reset/disqus_reset.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Disqus AJAX Test Site 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 43 | 44 | blog comments powered by Disqus 45 | 46 | 47 | -------------------------------------------------------------------------------- /snippets/php/batch_add_to_whitelist_from_csv.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |

Add users to whitelist

6 |

7 | Response: 8 |

9 | response as $email ) 37 | { 38 | echo "{$email->value} was added to the whitelist>
"; 39 | } 40 | 41 | } 42 | } 43 | ?> 44 | 45 | 46 | -------------------------------------------------------------------------------- /snippets/php/get-sso-username.php: -------------------------------------------------------------------------------- 1 | "; // Requires a registered DISQUS API application. Create one (free) at http://disqus.com/api/applications/ 5 | $remote_domain=""; // slug of the remote domain established at http://disqus.com/api/sso/ (the bolded word) 6 | $remote_identifier=""; // unique ID passed in the remote_auth_s3 payload 7 | // For more on unique ID see http://docs.disqus.com/developers/sso/ > "Using HMAC-SHA1 to pass user data" > "The message body (Base64-encoded)" > "id" 8 | 9 | // construct the query with our API key and the query we want to make 10 | // FORMAT: user=remote:remote_domain-remote_identifier 11 | $endpoint = 'http://disqus.com/api/3.0/users/details.json?api_key='.urlencode($key).'&user=remote:'.$remote_domain.'-'.$remote_identifier; 12 | 13 | // setup curl to make a call to the endpoint 14 | $session = curl_init($endpoint); 15 | 16 | // indicates that we want the response back rather than just returning a "TRUE" string 17 | curl_setopt($session, CURLOPT_RETURNTRANSFER, true); 18 | 19 | // execute GET and get the session back 20 | $result = curl_exec($session); 21 | 22 | // close connection 23 | curl_close($session); 24 | 25 | // decode the json data to make it easier to parse the php 26 | $results = json_decode($result); 27 | if ($results === NULL) die('Error parsing json'); 28 | 29 | // show the response in the browser 30 | // var_dump($results); 31 | 32 | $sso_username = $results->response->username; 33 | echo $sso_username; 34 | 35 | ?> 36 | -------------------------------------------------------------------------------- /snippets/php/get-thread-details.php: -------------------------------------------------------------------------------- 1 | "; // Requires a registered DISQUS API application. Create one (free) at http://disqus.com/api/applications/ 5 | $thread=""; 6 | $forum=""; 7 | 8 | // construct the query with our apikey and the query we want to make 9 | // Change api_key to api_secret when using your secret key 10 | /* 11 | DIFFERENT TYPES OF THREAD LOOKUPS: 12 | 1. By DISQUS thread ID (default): thread=%s — thread IDs are universally unique in DISQUS, so you can remove 'forum' param if you like 13 | 2. By identifier: thread:ident=%s — requires the forum parameter 14 | 3. By URL: thread:link=%s — requires the forum parameter 15 | */ 16 | $endpoint = 'http://disqus.com/api/3.0/threads/details.json?api_key='.urlencode($key).'&forum='.$forum.'&thread='.urlencode($thread); 17 | 18 | // setup curl to make a call to the endpoint 19 | $session = curl_init($endpoint); 20 | 21 | // indicates that we want the response back rather than just returning a "TRUE" string 22 | curl_setopt($session, CURLOPT_RETURNTRANSFER, true); 23 | 24 | // execute GET and get the session back 25 | $result = curl_exec($session); 26 | 27 | // close connection 28 | curl_close($session); 29 | 30 | // show the response in the browser 31 | var_dump($result); 32 | 33 | ?> -------------------------------------------------------------------------------- /snippets/php/list-100-most-active-users.php: -------------------------------------------------------------------------------- 1 | "; // Requires a registered DISQUS API application. Create one (free) at http://disqus.com/api/applications/ 6 | $forum=""; 7 | $limit="100"; // list 100 users. max is 100 8 | 9 | // construct the query with our API key and the query we want to make 10 | $endpoint = 'http://disqus.com/api/3.0/forums/listMostActiveUsers.json?api_secret='.urlencode($key).'&forum='.$forum.'&limit='.$limit; 11 | 12 | // setup curl to make a call to the endpoint 13 | $session = curl_init($endpoint); 14 | 15 | // indicates that we want the response back rather than just returning a "TRUE" string 16 | curl_setopt($session, CURLOPT_RETURNTRANSFER, true); 17 | 18 | // execute GET and get the session back 19 | $data = curl_exec($session); 20 | 21 | // close connection 22 | curl_close($session); 23 | 24 | // decode the json data to make it easier to parse the php 25 | $results = json_decode($data); 26 | if ($results === NULL) die('Error parsing json'); 27 | $users = $results->response; 28 | 29 | foreach ($users as $user) { 30 | echo $user->username.",".$user->numPosts."
"; 31 | } 32 | 33 | ?> 34 | -------------------------------------------------------------------------------- /snippets/php/list-all-replies-to-a-parent.php: -------------------------------------------------------------------------------- 1 | '; // get keys at http://disqus.com/api/ — can be public or secret for this endpoint 8 | $parentPost = ''; 9 | 10 | // first we need to determine the thread ID of the post in question 11 | $endpoint = 'https://disqus.com/api/3.0/posts/details?api_key='.$apikey.'&post='.$parentPost; 12 | 13 | // create a new cURL resource 14 | $session = curl_init($endpoint); 15 | 16 | // set URL and other appropriate options 17 | curl_setopt($session, CURLOPT_RETURNTRANSFER, 1); // instead of just returning true on success, return the result on success 18 | 19 | // set threads info 20 | $data = curl_exec($session); 21 | 22 | // close cURL resource, and free up system resources 23 | curl_close($session); 24 | 25 | // decode the json data to make it easier to parse the php 26 | $results = json_decode($data); 27 | if ($results === NULL) die('Error parsing JSON'); 28 | 29 | // grab parent post's details 30 | $thread = $results->response->thread; 31 | 32 | // now that we know the thread ID, let's grab all of its posts 33 | 34 | $limit = '100'; // max is 100 for this endpoint. 25 is default 35 | $order = 'asc'; // asc = oldest to newest. default is desc 36 | $since = $thread->createdAt; // leave alone to get all reply comments. otherwise set as UNIX timestamp 37 | 38 | $endpoint = 'https://disqus.com/api/3.0/threads/listPosts?api_key='.$apikey.'&thread='.$thread.'&limit='.$limit.'&order='.$order.'&since='.$since; 39 | 40 | $cursorMaxCount=0; 41 | list100Posts($endpoint,$cursor,$cursorMaxCount); 42 | 43 | function list100Posts($endpoint,$cursor,$cursorMaxCount) { 44 | global $parentPost; 45 | 46 | $session = curl_init($endpoint.$cursor); 47 | curl_setopt($session, CURLOPT_RETURNTRANSFER, 1); 48 | $data = curl_exec($session); 49 | curl_close($session); 50 | $results = json_decode($data); 51 | if ($results === NULL) die('Error parsing JSON'); 52 | 53 | $posts = $results->response; 54 | 55 | $cursor = $results->cursor; 56 | 57 | /* What the cursor array looks like: 58 | "cursor": { 59 | "prev": null, 60 | "hasNext": true, 61 | "next": "1320136178861708:0:0", 62 | "hasPrev": false, 63 | "total": null, 64 | "id": "1320136178861708:0:0", 65 | "more": true 66 | } 67 | */ 68 | 69 | $i=0; 70 | foreach ($posts as $post) { 71 | if ($post->parent == $parentPost) { 72 | echo "

Posted at ".$post->createdAt." by ".$post->author->name." (".$post->author->profileUrl.") :"; 73 | echo "

".$post->message."
"; 74 | // uncomment to output results to .csv 75 | /* 76 | header('Content-type: text/csv'); 77 | header('Content-disposition: attachment; filename="replies.csv"'); 78 | $fp = fopen('php://output', 'w+'); 79 | fwrite($fp, $post->createdAt); 80 | fwrite($fp, ","); 81 | fwrite($fp, $post->author->name); 82 | fwrite($fp, ","); 83 | fwrite($fp, $post->author->profileUrl); 84 | fwrite($fp, ","); 85 | fwrite($fp, $post->message); 86 | fwrite($fp, "\r\n"); 87 | fclose($fp); 88 | */ 89 | } 90 | $i++; 91 | } 92 | 93 | // cursor through until today 94 | if ($i == 100) { 95 | $cursor = $cursor->next; 96 | $i = 0; 97 | list100Posts($endpoint,$cursor); 98 | /* uncomment to only run $cursorMaxCount number of iterations 99 | $cursorMaxCount++; 100 | if ($cursorMaxCount < 10) { 101 | list100Threads($endpoint,$cursor,$cursorMaxCount); 102 | }*/ 103 | } 104 | } 105 | 106 | ?> -------------------------------------------------------------------------------- /snippets/php/list-all-threads-between-date-and-now.php: -------------------------------------------------------------------------------- 1 | '; // get keys at http://disqus.com/api/ — can be public or secret for this endpoint 6 | $forum = ''; 7 | $limit = '100'; // max is 100 for this endpoint. 25 is default 8 | $order = 'asc'; // asc = oldest to newest. default is desc 9 | $since = '1320123600'; // 1320123600 = nov 1, 2011 midnight EST 10 | 11 | $endpoint = 'https://disqus.com/api/3.0/threads/list?api_key='.$apikey.'&forum='.$forum.'&limit='.$limit.'&order='.$order.'&since='.$since."&cursor=".$cursor; 12 | 13 | $j=0; 14 | list100Threads($endpoint,$cursor,$j); 15 | 16 | function list100Threads($endpoint,$cursor,$j) { 17 | //echo "Endpoint is ".$endpoint."
"; 18 | //echo "Cursor is ".$cursor; 19 | // create a new cURL resource 20 | $session = curl_init($endpoint.$cursor); 21 | 22 | // set URL and other appropriate options 23 | curl_setopt($session, CURLOPT_RETURNTRANSFER, 1); // instead of just returning true on success, return the result on success 24 | 25 | // set threads info 26 | $data = curl_exec($session); 27 | 28 | // close cURL resource, and free up system resources 29 | curl_close($session); 30 | 31 | // decode the json data to make it easier to parse the php 32 | $results = json_decode($data); 33 | if ($results === NULL) die('Error parsing json'); 34 | 35 | // grab threads 36 | $threads = $results->response; 37 | 38 | // grab the current cursor 39 | $cursor = $results->cursor; 40 | //var_dump($cursor); 41 | 42 | /* What the cursor array looks like: 43 | "cursor": { 44 | "prev": null, 45 | "hasNext": true, 46 | "next": "1320136178861708:0:0", 47 | "hasPrev": false, 48 | "total": null, 49 | "id": "1320136178861708:0:0", 50 | "more": true 51 | } 52 | */ 53 | 54 | //echo "
    "; 55 | $i=0; 56 | foreach ($threads as $thread) { 57 | $url = $thread->link; 58 | $count = $thread->posts; 59 | $created = $thread->createdAt; 60 | //echo "
  • ".$created." ".$url." ".$count."
  • "; 61 | // output to csv 62 | header('Content-type: text/csv'); 63 | header('Content-disposition: attachment; filename="threads.csv"'); 64 | $fp = fopen('php://output', 'w+'); 65 | fwrite($fp, $created); 66 | fwrite($fp, ","); 67 | fwrite($fp, $url); 68 | fwrite($fp, ","); 69 | fwrite($fp, $count); 70 | fwrite($fp, "\r\n"); 71 | fclose($fp); 72 | $i++; 73 | } 74 | //echo "
"; 75 | 76 | // cursor through until today 77 | if ($i == 100) { 78 | $cursor = $cursor->next; 79 | $i = 0; 80 | list100Threads($endpoint,$cursor); 81 | /* uncomment to only run $j number of iterations 82 | $j++; 83 | if ($j < 10) { 84 | list100Threads($endpoint,$cursor,$j); 85 | }*/ 86 | } 87 | } 88 | 89 | ?> -------------------------------------------------------------------------------- /snippets/php/open-threads-since-date.php: -------------------------------------------------------------------------------- 1 | '; // get keys at http://disqus.com/api/ — can be public or secret for this endpoint 6 | $forum = ''; 7 | $limit = '100'; // max is 100 for this endpoint. 25 is default 8 | $order = 'asc'; // asc = oldest to newest. default is desc 9 | $since = '1333256400'; // 1333256400 = april 4, 2012 midnight EST 10 | 11 | $endpoint = 'https://disqus.com/api/3.0/threads/list?api_secret='.$apikey.'&forum='.$forum.'&limit='.$limit.'&order='.$order.'&since='.$since."&cursor=".$cursor; 12 | 13 | $cursorMaxCount=0; 14 | $totalThreads=0; 15 | $closedThreads=0; 16 | list100Threads($endpoint,$cursor,$cursorMaxCount); 17 | 18 | function list100Threads($endpoint,$cursor,$cursorMaxCount) { 19 | global $apikey, $totalThreads, $closedThreads; 20 | // create a new cURL resource 21 | $session = curl_init($endpoint.$cursor); 22 | 23 | // set URL and other appropriate options 24 | curl_setopt($session, CURLOPT_RETURNTRANSFER, 1); // instead of just returning true on success, return the result on success 25 | 26 | // set threads info 27 | $data = curl_exec($session); 28 | 29 | // close cURL resource, and free up system resources 30 | curl_close($session); 31 | 32 | // decode the json data to make it easier to parse the php 33 | $results = json_decode($data); 34 | if ($results === NULL) die('Error parsing JSON'); 35 | 36 | // grab threads 37 | $threads = $results->response; 38 | 39 | // grab the current cursor 40 | $cursor = $results->cursor; 41 | 42 | /* What the cursor array looks like: 43 | "cursor": { 44 | "prev": null, 45 | "hasNext": true, 46 | "next": "1320136178861708:0:0", 47 | "hasPrev": false, 48 | "total": null, 49 | "id": "1320136178861708:0:0", 50 | "more": true 51 | } 52 | */ 53 | 54 | $i=0; 55 | foreach ($threads as $thread) { 56 | $id = $thread->id; 57 | echo "Processing thread ".$id."... "; 58 | // first let's find out if the thread is already open or not 59 | $getThreadDetails = 'https://disqus.com/api/3.0/threads/details?api_secret='.$apikey.'&thread='.$id; 60 | $threadDetailsSession = curl_init($getThreadDetails); 61 | curl_setopt($threadDetailsSession, CURLOPT_RETURNTRANSFER, 1); // instead of just returning true on success, return the result on success 62 | $data = curl_exec($threadDetailsSession); 63 | curl_close($threadDetailsSession); 64 | // decode the json data to make it easier to parse the php 65 | $result = json_decode($data); 66 | if ($result === NULL) die('Error parsing JSON'); 67 | $thread = $result->response; 68 | if ($thread->isClosed == false) { 69 | echo "Thread is already open. Skipping.
"; 70 | } else { 71 | // open the thread 72 | $openThread = 'https://disqus.com/api/3.0/threads/open?api_secret='.$apikey.'&thread='.$id; 73 | $openThreadSession = curl_init($openThread); 74 | curl_setopt($openThreadSession,CURLOPT_POST,1); 75 | curl_setopt($openThreadSession,CURLOPT_POSTFIELDS,''); 76 | curl_setopt($openThreadSession,CURLOPT_RETURNTRANSFER,1); // prevents the output from being displayed on the pgae 77 | $result = curl_exec($openThreadSession); 78 | curl_close($openThreadSession); 79 | echo "Thread was closed. Opened.
"; 80 | $closedThreads++; 81 | } 82 | $totalThreads++; 83 | $i++; 84 | } 85 | 86 | // cursor through until today 87 | if ($i == 100) { 88 | $cursor = $cursor->next; 89 | $i = 0; 90 | list100Threads($endpoint,$cursor); 91 | /* uncomment to only run $cursorMaxCount number of iterations 92 | $cursorMaxCount++; 93 | if ($cursorMaxCount < 10) { 94 | list100Threads($endpoint,$cursor,$cursorMaxCount); 95 | }*/ 96 | } 97 | } 98 | 99 | echo "
Out of ".$totalThreads." threads, ".$closedThreads." were closed. They are now open. Blind, but now they see."; 100 | 101 | ?> -------------------------------------------------------------------------------- /snippets/php/single_access_token.php: -------------------------------------------------------------------------------- 1 | '); 3 | define('DISQUS_PUBLIC_KEY', ''); 4 | 5 | extract($_POST); 6 | 7 | $thread_id = ""; 8 | $access_token = ""; 9 | 10 | $url = 'https://disqus.com/api/3.0/threads/close.json'; 11 | $fields = array( 12 | 'access_token'=>urlencode($access_token), 13 | 'api_key'=>urlencode(DISQUS_PUBLIC_KEY), 14 | 'api_secret'=>urlencode(DISQUS_SECRET_KEY), 15 | 'thread'=>urlencode($thread_id) 16 | ); 17 | 18 | //url-ify the data for the POST 19 | foreach($fields as $key=>$value) { $fields_string .= $key.'='.$value.'&'; } 20 | rtrim($fields_string); 21 | 22 | //open connection 23 | $ch = curl_init(); 24 | 25 | //set the url, number of POST vars, POST data 26 | curl_setopt($ch,CURLOPT_URL,$url); 27 | curl_setopt($ch,CURLOPT_POST,count($fields)); 28 | curl_setopt($ch,CURLOPT_POSTFIELDS,$fields_string); 29 | 30 | //execute post 31 | $result = curl_exec($ch); 32 | 33 | var_dump($url); 34 | 35 | //close connection 36 | curl_close($ch); 37 | 38 | ?> 39 | -------------------------------------------------------------------------------- /snippets/php/sso_test_recipe.php: -------------------------------------------------------------------------------- 1 | '); 3 | define('DISQUS_PUBLIC_KEY', ''); 4 | 5 | $data = array( 6 | "id" => "999", 7 | "username" => "Disqus Test", 8 | "email" => "disqus-test@disqus.com", 9 | "avatar" => "http://dl.dropbox.com/u/31679327/Screenshots/30v.png", 10 | "url" => "http://disqus.com" 11 | ); 12 | 13 | $message = base64_encode(json_encode($data)); 14 | $timestamp = time(); 15 | $hmac = hash_hmac('sha1', "$message $timestamp", DISQUS_SECRET_KEY); 16 | ?> 17 | 18 | 19 | 20 | 21 | Test Site 22 | 23 | 24 | 25 |
26 | 27 | 43 | 44 | blog comments powered by Disqus 45 | 46 | 47 | -------------------------------------------------------------------------------- /snippets/php/update-thread-title.php: -------------------------------------------------------------------------------- 1 | '; 9 | $accessToken = ''; 10 | $fields_string = ''; // DO NOT EDIT 11 | 12 | // set POST variables 13 | $url = 'https://disqus.com/api/3.0/threads/update.json'; 14 | 15 | $fields = array( 16 | 'api_secret' => urlencode($api), 17 | 'access_token' => $accessToken, 18 | 'thread' => $threadId, 19 | 'title' => urlencode($newTitle), 20 | 'forum' => $shortname, 21 | ); 22 | 23 | foreach($fields as $key=>$value) { $fields_string .= $key.'='.$value.'&'; } 24 | rtrim($fields_string,'&'); 25 | $ch = curl_init(); 26 | curl_setopt($ch,CURLOPT_URL,$url); 27 | curl_setopt($ch,CURLOPT_POST,count($fields)); 28 | curl_setopt($ch,CURLOPT_POSTFIELDS,$fields_string); 29 | 30 | // execute POST 31 | $result = curl_exec($ch); 32 | 33 | // close connection 34 | curl_close($ch); 35 | 36 | // Show new information 37 | var_dump($result); 38 | } 39 | 40 | // Example function call 41 | // updateThreadTitle('12345678', 'example', 'This is the article\'s new title'); 42 | 43 | ?> -------------------------------------------------------------------------------- /snippets/php/user-details-from-comment-id.php: -------------------------------------------------------------------------------- 1 | response->author->username; 23 | $email = $results->response->author->email; // This will be blank if you aren't using the right access_token, or are not using the secret key 24 | $display_name = $results->response->author->name; 25 | $avatar_url = $results->response->author->avatar->cache; 26 | 27 | // Return it formatted 28 | echo '

  • '.$display_name.'
  • '.$username.'
  • '.$email.'
'; 29 | 30 | ?> -------------------------------------------------------------------------------- /sso/coldfusion/sso.cfm: -------------------------------------------------------------------------------- 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 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | var intTimestamp = VARIABLES.intUnixTimestamp + 8 * 60 * 60; //add an 8 hour offset to the Unix Timestamp 35 | var stgData = ""; 36 | var stgMessage = ""; 37 | var stgSignature = ""; 38 | 39 | 40 | 41 | 42 | 43 | stgData = SerializeJSON(VARIABLES.stcMemberInfo); 44 | stgMessage = ToBase64(stgData) & " " & intTimestamp; 45 | stgSignature = DISQUS_HMAC_SHA1( VARIABLES.stgPrivateKey, stgMessage); 46 | return ToBase64(stgData) & " " & stgSignature & " " & intTimestamp; 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 76 | 77 | ' > 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /sso/cs/DisqusSSO.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Web.Script.Serialization; 3 | using System.Security.Cryptography; 4 | using System.Text; 5 | 6 | namespace Disqus.Examples 7 | { 8 | public static class SSO 9 | { 10 | /// 11 | /// This class generates the payload we need to authenticate users remotely through Disqus 12 | /// This requires the Disqus SSO package and to have set up your application/remote domain properly 13 | /// See here for more: http://help.disqus.com/customer/portal/articles/236206-integrating-single-sign-on 14 | /// 15 | /// Usage: 16 | /// After inputting user data, a final payload will be generated which you use for the javascript variable 'remote_auth_s3' 17 | /// 18 | /// Markup: 19 | /// ------ 20 | /// var disqus_config = function () { 21 | /// this.page.remote_auth_s3 = '<%= Payload %>'; 22 | /// this.page.api_key = 'DISQUS_PUBLIC_KEY'; // TODO enter your API public key 23 | /// } 24 | /// 25 | /// Code-behind: 26 | /// ----------- 27 | /// string Payload = Disqus.Examples.SSO.GetPayload("test1", "Charlie Chaplin", "charlie@example.com"); 28 | /// 29 | /// 30 | 31 | /// Disqus API secret key can be obtained here: http://disqus.com/api/applications/ 32 | /// This will only work if that key is associated with your SSO remote domain 33 | /// It is highly recommended that you DO NOT hard-code your API secret key here, and instead read it from a secure configuration store 34 | 35 | private const string _apiSecret = "DISQUS_SECRET_KEY"; // TODO enter your API secret key (for illustrative purposes only) 36 | 37 | /// 38 | /// Gets the Disqus SSO payload to authenticate users 39 | /// 40 | /// The unique ID to associate with the user 41 | /// Non-unique name shown next to comments. 42 | /// User's email address, defined by RFC 5322 43 | /// URL of the avatar image 44 | /// Website, blog or custom profile URL for the user, defined by RFC 3986 45 | /// A string containing the signed payload 46 | public static string GetPayload(string user_id, string user_name, string user_email, string avatar_url = "", string website_url = "") 47 | { 48 | var userdata = new 49 | { 50 | id = user_id, 51 | username = user_name, 52 | email = user_email, 53 | avatar = avatar_url, 54 | url = website_url 55 | }; 56 | 57 | string serializedUserData = new JavaScriptSerializer().Serialize(userdata); 58 | return GeneratePayload(serializedUserData); 59 | } 60 | 61 | /// 62 | /// Method to log out a user from SSO 63 | /// 64 | /// A signed, empty payload string 65 | public static string LogoutUser() 66 | { 67 | var userdata = new { }; 68 | string serializedUserData = new JavaScriptSerializer().Serialize(userdata); 69 | return GeneratePayload(serializedUserData); 70 | } 71 | 72 | private static string GeneratePayload(string serializedUserData) 73 | { 74 | byte[] userDataAsBytes = Encoding.UTF8.GetBytes(serializedUserData); 75 | 76 | // Base64 Encode the message 77 | string Message = System.Convert.ToBase64String(userDataAsBytes); 78 | 79 | // Get the proper timestamp 80 | TimeSpan ts = (DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0)); 81 | string Timestamp = Convert.ToInt32(ts.TotalSeconds).ToString(); 82 | 83 | // Convert the message + timestamp to bytes 84 | byte[] messageAndTimestampBytes = Encoding.UTF8.GetBytes(Message + " " + Timestamp); 85 | 86 | // Convert Disqus API key to HMAC-SHA1 signature 87 | byte[] apiBytes = Encoding.UTF8.GetBytes(_apiSecret); 88 | using (HMACSHA1 hmac = new HMACSHA1(apiBytes)) { 89 | byte[] hashedMessage = hmac.ComputeHash(messageAndTimestampBytes); 90 | 91 | // Put it all together into the final payload 92 | return Message + " " + ByteToString(hashedMessage) + " " + Timestamp; 93 | } 94 | } 95 | 96 | private static string ByteToString(byte[] buff) 97 | { 98 | string sbinary = ""; 99 | 100 | for (int i = 0; i < buff.Length; i++) 101 | { 102 | sbinary += buff[i].ToString("X2"); // hex format 103 | } 104 | return (sbinary); 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /sso/elixir/sso.ex: -------------------------------------------------------------------------------- 1 | defmodule Disqus.SSO do 2 | @moduledoc """ 3 | This module depends on Jason (https://github.com/michalmuskala/jason) 4 | Add to the mix.exs deps section the folowing line 5 | {:jason, "~> 1.2"} 6 | 7 | like: 8 | defp deps do 9 | [ 10 | {:jason, "~> 1.2"} 11 | ] 12 | end 13 | """ 14 | 15 | 16 | @doc """ 17 | user_params - a map contains: 18 | - required keys "id", "username", "email", 19 | - optional keys "avatar", "url" 20 | """ 21 | def get_disqus_sso(user_params, disqus_api_key \\ nil, disqus_secret_key \\ nil) do 22 | # Getting keys from system env 23 | disqus_api_key = disqus_api_key || System.get_env("DISQUS_API_KEY") 24 | disqus_secret_key = disqus_secret_key || System.get_env("DISQUS_SECRET_KEY") 25 | 26 | # create a JSON packet of our data attributes 27 | # and base64 encode 28 | message = %{ 29 | "id" => user_params["id"], 30 | "username" => user_params["username"], 31 | "email" => user_params["email"], 32 | "avatar" => user_params["avatar"], 33 | "url" => user_params["url"] 34 | } 35 | |> Jason.encode!() 36 | |> Base.encode64() 37 | 38 | # generate a timestamp for signing the message 39 | timestamp = :os.system_time(:seconds) 40 | # generate our hmac signature 41 | hmac = :crypto.hmac(:sha, disqus_secret_key, "#{message} #{timestamp}") |> Base.encode16() 42 | 43 | # return a script tag to insert the sso message 44 | """ 45 | 51 | """ 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /sso/java/sso.java: -------------------------------------------------------------------------------- 1 | /* 2 | This script will calculate the Disqus SSO payload package 3 | Please see the Integrating SSO guide to find out how to configure your account first: 4 | http://help.disqus.com/customer/portal/articles/236206 5 | 6 | This example uses the Jackson JSON processor: http://jackson.codehaus.org/Home 7 | */ 8 | String DISQUS_SECRET_KEY = ""; // Your Disqus secret key from http://disqus.com/api/applications/ 9 | 10 | // User data, replace values with authenticated user data 11 | HashMap message = new HashMap(); 12 | message.put("id","uniqueId_123456789"); 13 | message.put("username","Charlie Chaplin"); 14 | message.put("email","charlie.chaplin@example.com"); 15 | //message.put("avatar","http://example.com/path-to-avatar.jpg"); // User's avatar URL (optional) 16 | //message.put("url","http://example.com/"); // User's website or profile URL (optional) 17 | 18 | // Encode user data 19 | ObjectMapper mapper = new ObjectMapper(); 20 | 21 | String jsonMessage = mapper.writeValueAsString(message); 22 | 23 | String base64EncodedStr = new String(Base64.encodeBase64(jsonMessage.getBytes())); 24 | 25 | // Get the timestamp 26 | long timestamp = System.currentTimeMillis()/1000; 27 | 28 | // Assemble the HMAC-SHA1 signature 29 | String signature = calculateRFC2104HMAC(base64EncodedStr + " " + timestamp, DISQUS_SECRET_KEY); 30 | 31 | // Output string to use in remote_auth_s3 variable 32 | System.out.println(base64EncodedStr + " " + signature + " " + timestamp); 33 | 34 | private static String toHexString(byte[] bytes) 35 | { 36 | Formatter formatter = new Formatter(); 37 | for (byte b : bytes) 38 | { 39 | formatter.format("%02x", b); 40 | } 41 | 42 | return formatter.toString(); 43 | } 44 | 45 | public static String calculateRFC2104HMAC(String data, String key) 46 | throws SignatureException, NoSuchAlgorithmException, InvalidKeyException 47 | { 48 | private final String HMAC_SHA1_ALGORITHM = "HmacSHA1"; 49 | SecretKeySpec signingKey = new SecretKeySpec(key.getBytes(), HMAC_SHA1_ALGORITHM); 50 | Mac mac = Mac.getInstance(HMAC_SHA1_ALGORITHM); 51 | mac.init(signingKey); 52 | return toHexString(mac.doFinal(data.getBytes())); 53 | } -------------------------------------------------------------------------------- /sso/javascript/hmac-sha1-3.1.2.js: -------------------------------------------------------------------------------- 1 | /* 2 | CryptoJS v3.1.2 3 | code.google.com/p/crypto-js 4 | (c) 2009-2013 by Jeff Mott. All rights reserved. 5 | code.google.com/p/crypto-js/wiki/License 6 | */ 7 | var CryptoJS=CryptoJS||function(g,l){var e={},d=e.lib={},m=function(){},k=d.Base={extend:function(a){m.prototype=this;var c=new m;a&&c.mixIn(a);c.hasOwnProperty("init")||(c.init=function(){c.$super.init.apply(this,arguments)});c.init.prototype=c;c.$super=this;return c},create:function(){var a=this.extend();a.init.apply(a,arguments);return a},init:function(){},mixIn:function(a){for(var c in a)a.hasOwnProperty(c)&&(this[c]=a[c]);a.hasOwnProperty("toString")&&(this.toString=a.toString)},clone:function(){return this.init.prototype.extend(this)}}, 8 | p=d.WordArray=k.extend({init:function(a,c){a=this.words=a||[];this.sigBytes=c!=l?c:4*a.length},toString:function(a){return(a||n).stringify(this)},concat:function(a){var c=this.words,q=a.words,f=this.sigBytes;a=a.sigBytes;this.clamp();if(f%4)for(var b=0;b>>2]|=(q[b>>>2]>>>24-8*(b%4)&255)<<24-8*((f+b)%4);else if(65535>>2]=q[b>>>2];else c.push.apply(c,q);this.sigBytes+=a;return this},clamp:function(){var a=this.words,c=this.sigBytes;a[c>>>2]&=4294967295<< 9 | 32-8*(c%4);a.length=g.ceil(c/4)},clone:function(){var a=k.clone.call(this);a.words=this.words.slice(0);return a},random:function(a){for(var c=[],b=0;b>>2]>>>24-8*(f%4)&255;b.push((d>>>4).toString(16));b.push((d&15).toString(16))}return b.join("")},parse:function(a){for(var c=a.length,b=[],f=0;f>>3]|=parseInt(a.substr(f, 10 | 2),16)<<24-4*(f%8);return new p.init(b,c/2)}},j=b.Latin1={stringify:function(a){var c=a.words;a=a.sigBytes;for(var b=[],f=0;f>>2]>>>24-8*(f%4)&255));return b.join("")},parse:function(a){for(var c=a.length,b=[],f=0;f>>2]|=(a.charCodeAt(f)&255)<<24-8*(f%4);return new p.init(b,c)}},h=b.Utf8={stringify:function(a){try{return decodeURIComponent(escape(j.stringify(a)))}catch(c){throw Error("Malformed UTF-8 data");}},parse:function(a){return j.parse(unescape(encodeURIComponent(a)))}}, 11 | r=d.BufferedBlockAlgorithm=k.extend({reset:function(){this._data=new p.init;this._nDataBytes=0},_append:function(a){"string"==typeof a&&(a=h.parse(a));this._data.concat(a);this._nDataBytes+=a.sigBytes},_process:function(a){var c=this._data,b=c.words,f=c.sigBytes,d=this.blockSize,e=f/(4*d),e=a?g.ceil(e):g.max((e|0)-this._minBufferSize,0);a=e*d;f=g.min(4*a,f);if(a){for(var k=0;ka;a++){if(16>a)m[a]=d[e+a]|0;else{var c=m[a-3]^m[a-8]^m[a-14]^m[a-16];m[a]=c<<1|c>>>31}c=(n<<5|n>>>27)+l+m[a];c=20>a?c+((j&h|~j&g)+1518500249):40>a?c+((j^h^g)+1859775393):60>a?c+((j&h|j&g|h&g)-1894007588):c+((j^h^ 15 | g)-899497514);l=g;g=h;h=j<<30|j>>>2;j=n;n=c}b[0]=b[0]+n|0;b[1]=b[1]+j|0;b[2]=b[2]+h|0;b[3]=b[3]+g|0;b[4]=b[4]+l|0},_doFinalize:function(){var d=this._data,e=d.words,b=8*this._nDataBytes,g=8*d.sigBytes;e[g>>>5]|=128<<24-g%32;e[(g+64>>>9<<4)+14]=Math.floor(b/4294967296);e[(g+64>>>9<<4)+15]=b;d.sigBytes=4*e.length;this._process();return this._hash},clone:function(){var e=d.clone.call(this);e._hash=this._hash.clone();return e}});g.SHA1=d._createHelper(l);g.HmacSHA1=d._createHmacHelper(l)})(); 16 | (function(){var g=CryptoJS,l=g.enc.Utf8;g.algo.HMAC=g.lib.Base.extend({init:function(e,d){e=this._hasher=new e.init;"string"==typeof d&&(d=l.parse(d));var g=e.blockSize,k=4*g;d.sigBytes>k&&(d=e.finalize(d));d.clamp();for(var p=this._oKey=d.clone(),b=this._iKey=d.clone(),n=p.words,j=b.words,h=0;h $user["id"], 7 | "username" => $user["username"], 8 | "email" => $user["email"] 9 | ); 10 | 11 | function dsq_hmacsha1($data, $key) { 12 | $blocksize=64; 13 | $hashfunc='sha1'; 14 | if (strlen($key)>$blocksize) 15 | $key=pack('H*', $hashfunc($key)); 16 | $key=str_pad($key,$blocksize,chr(0x00)); 17 | $ipad=str_repeat(chr(0x36),$blocksize); 18 | $opad=str_repeat(chr(0x5c),$blocksize); 19 | $hmac = pack( 20 | 'H*',$hashfunc( 21 | ($key^$opad).pack( 22 | 'H*',$hashfunc( 23 | ($key^$ipad).$data 24 | ) 25 | ) 26 | ) 27 | ); 28 | return bin2hex($hmac); 29 | } 30 | 31 | $message = base64_encode(json_encode($data)); 32 | $timestamp = time(); 33 | $hmac = dsq_hmacsha1($message . ' ' . $timestamp, DISQUS_SECRET_KEY); 34 | ?> 35 | -------------------------------------------------------------------------------- /sso/python2/sso.py: -------------------------------------------------------------------------------- 1 | import base64 2 | import hashlib 3 | import hmac 4 | import simplejson 5 | import time 6 | 7 | DISQUS_SECRET_KEY = '123456' 8 | DISQUS_PUBLIC_KEY = 'abcdef' 9 | 10 | def get_disqus_sso(user): 11 | # create a JSON packet of our data attributes 12 | data = simplejson.dumps({ 13 | 'id': user['id'], 14 | 'username': user['username'], 15 | 'email': user['email'], 16 | }) 17 | # encode the data to base64 18 | message = base64.b64encode(data) 19 | # generate a timestamp for signing the message 20 | timestamp = int(time.time()) 21 | # generate our hmac signature 22 | sig = hmac.HMAC(DISQUS_SECRET_KEY, '%s %s' % (message, timestamp), hashlib.sha1).hexdigest() 23 | 24 | # return a script tag to insert the sso message 25 | return """""" % dict( 31 | message=message, 32 | timestamp=timestamp, 33 | sig=sig, 34 | pub_key=DISQUS_PUBLIC_KEY, 35 | ) -------------------------------------------------------------------------------- /sso/python3/sso.py: -------------------------------------------------------------------------------- 1 | import base64 2 | import hashlib 3 | import hmac 4 | import simplejson 5 | import time 6 | 7 | DISQUS_SECRET_KEY = '123456' 8 | DISQUS_PUBLIC_KEY = 'abcdef' 9 | 10 | def get_disqus_sso(user): 11 | # create a JSON packet of our data attributes 12 | data = simplejson.dumps({ 13 | 'username': user['username'], 14 | 'id': user['id'], 15 | 'email': user['email'], 16 | }) 17 | # encode the data to base64 18 | message = base64.b64encode(data.encode('utf-8')).decode() 19 | # generate a timestamp for signing the message 20 | timestamp = int(time.time()) 21 | # generate our hmac signature 22 | sig = hmac.HMAC(DISQUS_SECRET_KEY.encode('utf-8'), '{} {}'.format(message, timestamp).encode('utf-8'), hashlib.sha1).hexdigest() 23 | 24 | # return a script tag to insert the sso message 25 | return """""" % dict( 31 | message=message, 32 | timestamp=timestamp, 33 | sig=sig, 34 | pub_key=DISQUS_PUBLIC_KEY, 35 | ) 36 | -------------------------------------------------------------------------------- /sso/ruby/sso.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'base64' 3 | require 'cgi' 4 | require 'openssl' 5 | require "json" 6 | 7 | DISQUS_SECRET_KEY = '' 8 | DISQUS_PUBLIC_KEY = '' 9 | 10 | def get_disqus_sso(user) 11 | # create a JSON packet of our data attributes 12 | data = { 13 | 'id' => user['id'], 14 | 'username' => user['username'], 15 | 'email' => user['email'] 16 | #'avatar' => user['avatar'], 17 | #'url' => user['url'] 18 | }.to_json 19 | 20 | # encode the data to base64 21 | message = Base64.encode64(data).gsub("\n", "") 22 | # generate a timestamp for signing the message 23 | timestamp = Time.now.to_i 24 | # generate our hmac signature 25 | sig = OpenSSL::HMAC.hexdigest('sha1', DISQUS_SECRET_KEY, '%s %s' % [message, timestamp]) 26 | 27 | # return a script tag to insert the sso message 28 | return "" 34 | end 35 | -------------------------------------------------------------------------------- /sso/scala/Disqus.scala: -------------------------------------------------------------------------------- 1 | /* A Scala (Play) example 2 | * Adapted from the Java example */ 3 | 4 | package controllers 5 | 6 | import play.api.libs.json.Json 7 | import play.api.libs.json.Json._ 8 | 9 | // Don't use sun.misc as per 10 | // http://stackoverflow.com/questions/2267036/work-sun-misc-base64encoder-decoder-for-getting-byte 11 | import org.apache.commons.codec.binary.Base64 12 | import javax.crypto.Mac 13 | import javax.crypto.spec.SecretKeySpec 14 | 15 | import models.User 16 | 17 | object Hex { 18 | def valueOf(buf: Array[Byte]): String = buf.map("%02x" format _).mkString 19 | } 20 | 21 | object Disqus { 22 | 23 | val DISQUS_SECRET_KEY: String = "123456" 24 | val DISQUS_PUBLIC_KEY: String = "abcdef" 25 | 26 | def calculateRFC2104HMAC(data: String, key: String): String = { 27 | val HMAC_SHA1_ALGORITHM = "HmacSHA1" 28 | val signingKey = new SecretKeySpec(key.getBytes(), HMAC_SHA1_ALGORITHM) 29 | val mac = Mac.getInstance(HMAC_SHA1_ALGORITHM) 30 | mac.init(signingKey); 31 | Hex.valueOf(mac.doFinal(data.getBytes)) 32 | } 33 | 34 | def getMessage(user: User): (String, String) = { 35 | 36 | val message = toJson( 37 | Map("id" -> toJson(user.id), 38 | "username" -> toJson(user.name), 39 | "email" -> toJson(user.email))) 40 | 41 | val jsonMessage: String = Json.stringify(message) 42 | val base64EncodedStr: String = new String(Base64.encodeBase64(jsonMessage.getBytes())) 43 | val timestamp: Long = System.currentTimeMillis() / 1000 44 | 45 | val signature: String = calculateRFC2104HMAC(base64EncodedStr + ' ' + timestamp, DISQUS_SECRET_KEY) 46 | 47 | (base64EncodedStr + " " + signature + " " + timestamp, DISQUS_PUBLIC_KEY) 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /widgets/php/latest_comments.php: -------------------------------------------------------------------------------- 1 | response; 23 | 24 | foreach ($comments as $comment) 25 | { 26 | $finalResults .= 27 | '
28 | 29 |

'.$comment->author->name.'

30 |

'.$comment->message.'

31 |
'; 32 | } 33 | 34 | ?> 35 | 36 | 37 | 38 | 39 | Example Widget 40 | 56 | 57 | 58 |

PHP Version

59 |
Code 60 | 61 | 62 |

Javascript Version

63 |
64 | 65 | 66 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /widgets/php/popular_threads/config.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/disqus/DISQUS-API-Recipes/1b35cc3e2160110a94453a52eff364a1b9f4296b/widgets/php/popular_threads/config.php -------------------------------------------------------------------------------- /widgets/php/popular_threads/getfromcache.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /widgets/php/popular_threads/getpopularthreads.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | response; 37 | foreach ($threads as $thread) { 38 | $finalResults .= "

link."\">".$thread->title." (".$thread->posts.")

"; 39 | } 40 | 41 | // save api results to the cache file you specified in $filename 42 | file_put_contents($filename, $finalResults); 43 | 44 | // outputs the parsed HTML for your own viewing (optional) 45 | echo $finalResults; 46 | ?> 47 | 48 | 49 | 50 | --------------------------------------------------------------------------------