├── SuperTokensIOS ├── Assets │ └── .gitkeep └── Classes │ ├── .gitkeep │ ├── Version.swift │ ├── extensions │ ├── Dictionary.swift │ └── String.swift │ ├── Error.swift │ ├── UnauthorisedResponse.swift │ ├── Constants.swift │ ├── NormalisedURLDomain.swift │ ├── AntiCSRF.swift │ ├── NormalisedURLPath.swift │ ├── FrontToken.swift │ └── SuperTokens.swift ├── _Pods.xcodeproj ├── testHelpers ├── server │ ├── .gitignore │ ├── package.json │ ├── index.html │ └── utils.js ├── startServer └── testapp │ ├── Tests │ ├── SuperTokensSession_Tests-Bridging-Header.h │ ├── Info.plist │ ├── utilsTests.swift │ └── accessTokenTests.swift │ ├── Pods │ ├── Target Support Files │ │ ├── SuperTokensIOS │ │ │ ├── SuperTokensIOS.modulemap │ │ │ ├── SuperTokensIOS-dummy.m │ │ │ ├── SuperTokensIOS-prefix.pch │ │ │ ├── SuperTokensIOS-umbrella.h │ │ │ ├── SuperTokensIOS.debug.xcconfig │ │ │ ├── SuperTokensIOS.release.xcconfig │ │ │ └── SuperTokensIOS-Info.plist │ │ ├── Pods-SuperTokensSession_Tests │ │ │ ├── Pods-SuperTokensSession_Tests-acknowledgements.markdown │ │ │ ├── Pods-SuperTokensSession_Tests.modulemap │ │ │ ├── Pods-SuperTokensSession_Tests-dummy.m │ │ │ ├── Pods-SuperTokensSession_Tests-umbrella.h │ │ │ ├── Pods-SuperTokensSession_Tests.debug.xcconfig │ │ │ ├── Pods-SuperTokensSession_Tests.release.xcconfig │ │ │ ├── Pods-SuperTokensSession_Tests-Info.plist │ │ │ └── Pods-SuperTokensSession_Tests-acknowledgements.plist │ │ └── Pods-SuperTokensSession_Example │ │ │ ├── Pods-SuperTokensSession_Example.modulemap │ │ │ ├── Pods-SuperTokensSession_Example-dummy.m │ │ │ ├── Pods-SuperTokensSession_Example-umbrella.h │ │ │ ├── Pods-SuperTokensSession_Example-Info.plist │ │ │ ├── Pods-SuperTokensSession_Example.debug.xcconfig │ │ │ ├── Pods-SuperTokensSession_Example.release.xcconfig │ │ │ ├── Pods-SuperTokensSession_Example-frameworks.sh │ │ │ └── Pods-SuperTokensSession_Example-acknowledgements.markdown │ ├── Pods.xcodeproj │ │ └── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ ├── Manifest.lock │ └── Local Podspecs │ │ ├── SuperTokensSession.podspec.json │ │ └── SuperTokensIOS.podspec.json │ ├── SuperTokensSession.xcodeproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── SuperTokensSession-Example.xcscheme │ ├── Podfile │ ├── SuperTokensSession.xcworkspace │ ├── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── contents.xcworkspacedata │ ├── Podfile.lock │ └── SuperTokensSession │ ├── ViewController.swift │ ├── Images.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json │ ├── Info.plist │ ├── Base.lproj │ ├── Main.storyboard │ └── LaunchScreen.xib │ └── AppDelegate.swift ├── setup-pre-commit ├── examples └── with-thirdparty │ ├── with-thirdparty │ ├── Assets.xcassets │ │ ├── Contents.json │ │ ├── AccentColor.colorset │ │ │ └── Contents.json │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Constants.swift │ ├── with-thirdparty.entitlements │ ├── ViewController.swift │ ├── Info.plist │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── LoginScreen │ │ ├── GithubLoginURLProtocol.swift │ │ ├── LoginView.xib │ │ └── LoginScreenViewController.swift │ ├── HomeScreen │ │ └── HomeScreenViewController.swift │ ├── SceneDelegate.swift │ └── AppDelegate.swift │ ├── backend │ ├── package.json │ ├── index.ts │ ├── config.ts │ └── tsconfig.json │ └── README.md ├── frontendDriverInterfaceSupported.json ├── .circleci ├── generateConfig.sh ├── updateDocsInWebsite ├── markDevTagAsTestNotPassed.sh ├── markAsSuccess.sh ├── config.yml ├── doTests.sh ├── setupAndTestWithFreeCore.sh └── config_continue.yml ├── .github ├── workflows │ ├── github-actions-changelog.yml │ ├── lint-pr-title.yml │ └── pre-commit-hook-run.yml └── PULL_REQUEST_TEMPLATE.md ├── README.md ├── Package.swift ├── SuperTokensIOS.podspec ├── hooks └── pre-commit.sh ├── .gitignore ├── addDevTag ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── CHANGELOG.md ├── addReleaseTag └── LICENSE.md /SuperTokensIOS/Assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /SuperTokensIOS/Classes/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /_Pods.xcodeproj: -------------------------------------------------------------------------------- 1 | Example/Pods/Pods.xcodeproj -------------------------------------------------------------------------------- /testHelpers/server/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | package-lock.json -------------------------------------------------------------------------------- /setup-pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cp hooks/pre-commit.sh .git/hooks/pre-commit 3 | chmod +x .git/hooks/pre-commit -------------------------------------------------------------------------------- /examples/with-thirdparty/with-thirdparty/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /testHelpers/startServer: -------------------------------------------------------------------------------- 1 | # use: ./test/startServer ../../../com-root 2 | (cd testHelpers/server && TEST_MODE=testing INSTALL_PATH=../../$1 NODE_PORT=$2 node .) -------------------------------------------------------------------------------- /testHelpers/testapp/Tests/SuperTokensSession_Tests-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | -------------------------------------------------------------------------------- /testHelpers/testapp/Pods/Target Support Files/SuperTokensIOS/SuperTokensIOS.modulemap: -------------------------------------------------------------------------------- 1 | framework module SuperTokensIOS { 2 | umbrella header "SuperTokensIOS-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /testHelpers/testapp/Pods/Target Support Files/SuperTokensIOS/SuperTokensIOS-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_SuperTokensIOS : NSObject 3 | @end 4 | @implementation PodsDummy_SuperTokensIOS 5 | @end 6 | -------------------------------------------------------------------------------- /testHelpers/testapp/Pods/Pods.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /examples/with-thirdparty/with-thirdparty/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /testHelpers/testapp/Pods/Target Support Files/Pods-SuperTokensSession_Tests/Pods-SuperTokensSession_Tests-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | Generated by CocoaPods - https://cocoapods.org 4 | -------------------------------------------------------------------------------- /testHelpers/testapp/Pods/Target Support Files/Pods-SuperTokensSession_Tests/Pods-SuperTokensSession_Tests.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_SuperTokensSession_Tests { 2 | umbrella header "Pods-SuperTokensSession_Tests-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /testHelpers/testapp/SuperTokensSession.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /testHelpers/testapp/Podfile: -------------------------------------------------------------------------------- 1 | use_frameworks! 2 | 3 | platform :ios, '13.0' 4 | 5 | target 'SuperTokensSession_Example' do 6 | pod 'SuperTokensIOS', :path => '../../' 7 | 8 | target 'SuperTokensSession_Tests' do 9 | inherit! :search_paths 10 | 11 | 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /testHelpers/testapp/Pods/Target Support Files/Pods-SuperTokensSession_Example/Pods-SuperTokensSession_Example.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_SuperTokensSession_Example { 2 | umbrella header "Pods-SuperTokensSession_Example-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /testHelpers/testapp/Pods/Target Support Files/Pods-SuperTokensSession_Tests/Pods-SuperTokensSession_Tests-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_SuperTokensSession_Tests : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_SuperTokensSession_Tests 5 | @end 6 | -------------------------------------------------------------------------------- /examples/with-thirdparty/with-thirdparty/Constants.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Constants.swift 3 | // with-thirdparty 4 | // 5 | // Created by Nemi Shah on 10/11/23. 6 | // 7 | 8 | import Foundation 9 | 10 | class Constants { 11 | static let apiDomain = "http://192.168.29.87:3001" 12 | } 13 | -------------------------------------------------------------------------------- /testHelpers/testapp/Pods/Target Support Files/Pods-SuperTokensSession_Example/Pods-SuperTokensSession_Example-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_SuperTokensSession_Example : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_SuperTokensSession_Example 5 | @end 6 | -------------------------------------------------------------------------------- /examples/with-thirdparty/with-thirdparty/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "platform" : "ios", 6 | "size" : "1024x1024" 7 | } 8 | ], 9 | "info" : { 10 | "author" : "xcode", 11 | "version" : 1 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /testHelpers/testapp/Pods/Target Support Files/SuperTokensIOS/SuperTokensIOS-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 | -------------------------------------------------------------------------------- /testHelpers/testapp/SuperTokensSession.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /testHelpers/testapp/SuperTokensSession.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /testHelpers/testapp/Pods/Pods.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /frontendDriverInterfaceSupported.json: -------------------------------------------------------------------------------- 1 | { 2 | "_comment": "contains a list of frontend-backend interface versions that this package supports", 3 | "versions": [ 4 | "1.16", 5 | "1.17", 6 | "1.18", 7 | "1.19", 8 | "2.0", 9 | "3.0", 10 | "3.1", 11 | "4.0", 12 | "4.1" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /SuperTokensIOS/Classes/Version.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Version.swift 3 | // SuperTokensSession 4 | // 5 | // Created by Nemi Shah on 30/09/22. 6 | // 7 | 8 | import Foundation 9 | 10 | internal class Version { 11 | static let supported_fdi: [String] = ["1.16", "1.17", "1.18", "1.19", "2.0", "3.0", "3.1", "4.0", "4.1"] 12 | static let sdkVersion = "0.4.3" 13 | } 14 | -------------------------------------------------------------------------------- /examples/with-thirdparty/with-thirdparty/with-thirdparty.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.developer.applesignin 6 | 7 | Default 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.circleci/generateConfig.sh: -------------------------------------------------------------------------------- 1 | frontendDriverJson=`cat ../frontendDriverInterfaceSupported.json` 2 | frontendDriverArray=`echo $frontendDriverJson | jq ".versions"` 3 | 4 | if [ -z "$SUPERTOKENS_API_KEY" ]; then 5 | echo "SUPERTOKENS_API_KEY missing" 6 | exit 1; 7 | fi 8 | 9 | sed -i -e 's/fdi-version: placeholder/fdi-version: '`printf "%q" $frontendDriverArray`'/' config_continue.yml 10 | -------------------------------------------------------------------------------- /testHelpers/testapp/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - SuperTokensIOS (0.0.1) 3 | 4 | DEPENDENCIES: 5 | - SuperTokensIOS (from `../../`) 6 | 7 | EXTERNAL SOURCES: 8 | SuperTokensIOS: 9 | :path: "../../" 10 | 11 | SPEC CHECKSUMS: 12 | SuperTokensIOS: b95ce1a69bab8c77e8588a708071817caddcc33d 13 | 14 | PODFILE CHECKSUM: 38e350f7611d275dd34e93bb4479be1460d06e86 15 | 16 | COCOAPODS: 1.11.3 17 | -------------------------------------------------------------------------------- /testHelpers/testapp/Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - SuperTokensIOS (0.0.1) 3 | 4 | DEPENDENCIES: 5 | - SuperTokensIOS (from `../../`) 6 | 7 | EXTERNAL SOURCES: 8 | SuperTokensIOS: 9 | :path: "../../" 10 | 11 | SPEC CHECKSUMS: 12 | SuperTokensIOS: b95ce1a69bab8c77e8588a708071817caddcc33d 13 | 14 | PODFILE CHECKSUM: 38e350f7611d275dd34e93bb4479be1460d06e86 15 | 16 | COCOAPODS: 1.11.3 17 | -------------------------------------------------------------------------------- /SuperTokensIOS/Classes/extensions/Dictionary.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // 4 | // 5 | // Created by Matt Murray on 9/8/23. 6 | // 7 | 8 | import Foundation 9 | 10 | extension Dictionary where Key: ExpressibleByStringLiteral { 11 | public mutating func lowerCaseKeys() { 12 | for key in self.keys { 13 | self[String(describing: key).lowercased() as! Key] = self.removeValue(forKey: key) 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /testHelpers/testapp/Pods/Target Support Files/SuperTokensIOS/SuperTokensIOS-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 SuperTokensIOSVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char SuperTokensIOSVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /testHelpers/server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "version": "0.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "cookie-parser": "1.4.4", 13 | "cors": "^2.8.5", 14 | "express": "4.17.1", 15 | "morgan": "^1.10.0", 16 | "supertokens-node": "github:supertokens/supertokens-node#16.0" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /testHelpers/testapp/Pods/Target Support Files/Pods-SuperTokensSession_Tests/Pods-SuperTokensSession_Tests-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_SuperTokensSession_TestsVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_SuperTokensSession_TestsVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /.github/workflows/github-actions-changelog.yml: -------------------------------------------------------------------------------- 1 | name: "Enforcing changelog in PRs Workflow" 2 | on: 3 | pull_request: 4 | types: [opened, synchronize, reopened, ready_for_review, labeled, unlabeled] 5 | 6 | jobs: 7 | # Enforces the update of a changelog file on every pull request 8 | changelog: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | - uses: dangoslen/changelog-enforcer@v2 13 | with: 14 | changeLogPath: 'CHANGELOG.md' 15 | skipLabels: 'Skip-Changelog' -------------------------------------------------------------------------------- /testHelpers/testapp/Pods/Target Support Files/Pods-SuperTokensSession_Example/Pods-SuperTokensSession_Example-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_SuperTokensSession_ExampleVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_SuperTokensSession_ExampleVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /.github/workflows/lint-pr-title.yml: -------------------------------------------------------------------------------- 1 | name: "Lint PR Title" 2 | 3 | on: 4 | pull_request: 5 | types: 6 | - opened 7 | - reopened 8 | - edited 9 | - synchronize 10 | 11 | jobs: 12 | pr-title: 13 | name: Lint PR title 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: amannn/action-semantic-pull-request@v3 17 | env: 18 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 19 | with: 20 | validateSingleCommit: true -------------------------------------------------------------------------------- /.github/workflows/pre-commit-hook-run.yml: -------------------------------------------------------------------------------- 1 | name: "Pre commit hook check" 2 | 3 | on: 4 | pull_request: 5 | types: 6 | - opened 7 | - reopened 8 | - edited 9 | - synchronize 10 | 11 | jobs: 12 | pr-title: 13 | name: Pre commit hook check 14 | runs-on: ubuntu-latest 15 | container: rishabhpoddar/supertokens_website_sdk_testing_node_16 16 | steps: 17 | - uses: actions/checkout@v2 18 | - run: git init && git add --all && git -c user.name='test' -c user.email='test@example.com' commit -m 'init for pr action' 19 | - run: ./hooks/pre-commit.sh 20 | -------------------------------------------------------------------------------- /testHelpers/testapp/SuperTokensSession/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // SuperTokensSession 4 | // 5 | // Created by rishabhpoddar on 03/24/2020. 6 | // Copyright (c) 2020 rishabhpoddar. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | // Do any additional setup after loading the view, typically from a nib. 16 | } 17 | 18 | override func didReceiveMemoryWarning() { 19 | super.didReceiveMemoryWarning() 20 | // Dispose of any resources that can be recreated. 21 | } 22 | 23 | } 24 | 25 | -------------------------------------------------------------------------------- /examples/with-thirdparty/with-thirdparty/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // with-thirdparty 4 | // 5 | // Created by Nemi Shah on 10/11/23. 6 | // 7 | 8 | import UIKit 9 | import SuperTokensIOS 10 | 11 | class ViewController: UIViewController { 12 | override func viewDidAppear(_ animated: Bool) { 13 | if !SuperTokens.doesSessionExist() { 14 | self.navigationController?.pushViewController(LoginScreenViewController(nibName: "LoginView", bundle: nil), animated: true) 15 | } else { 16 | self.navigationController?.pushViewController(HomeScreenViewController(nibName: "HomeView", bundle: nil), animated: true) 17 | } 18 | } 19 | } 20 | 21 | -------------------------------------------------------------------------------- /testHelpers/testapp/Pods/Local Podspecs/SuperTokensSession.podspec.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "SuperTokensSession", 3 | "version": "1.3.1", 4 | "summary": "iOS SuperTokens SDK.", 5 | "description": "SuperTokens SDK for iOS written in Swift. This SDK takes care of managing a session on the frontend side.", 6 | "homepage": "https://github.com/supertokens/supertokens-ios", 7 | "license": { 8 | "type": "Apache 2.0", 9 | "file": "LICENSE.md" 10 | }, 11 | "authors": { 12 | "rishabhpoddar": "rishabh@supertokens.io" 13 | }, 14 | "source": { 15 | "git": "https://github.com/supertokens/supertokens-ios.git", 16 | "tag": "v1.3.1" 17 | }, 18 | "platforms": { 19 | "ios": "12.0" 20 | }, 21 | "source_files": "SuperTokensSession/Classes/**/*", 22 | "swift_versions": "5.0", 23 | "swift_version": "5.0" 24 | } 25 | -------------------------------------------------------------------------------- /testHelpers/testapp/Pods/Target Support Files/Pods-SuperTokensSession_Tests/Pods-SuperTokensSession_Tests.debug.xcconfig: -------------------------------------------------------------------------------- 1 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/SuperTokensIOS" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/SuperTokensIOS/SuperTokensIOS.framework/Headers" 5 | OTHER_LDFLAGS = $(inherited) -framework "SuperTokensIOS" 6 | PODS_BUILD_DIR = ${BUILD_DIR} 7 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 8 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 9 | PODS_ROOT = ${SRCROOT}/Pods 10 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates 11 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 12 | -------------------------------------------------------------------------------- /testHelpers/testapp/Pods/Target Support Files/Pods-SuperTokensSession_Tests/Pods-SuperTokensSession_Tests.release.xcconfig: -------------------------------------------------------------------------------- 1 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/SuperTokensIOS" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/SuperTokensIOS/SuperTokensIOS.framework/Headers" 5 | OTHER_LDFLAGS = $(inherited) -framework "SuperTokensIOS" 6 | PODS_BUILD_DIR = ${BUILD_DIR} 7 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 8 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 9 | PODS_ROOT = ${SRCROOT}/Pods 10 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates 11 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 12 | -------------------------------------------------------------------------------- /testHelpers/testapp/Tests/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 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SuperTokens iOS SDK 2 | 3 | 4 | chat on Discord 6 | 7 | ## About 8 | 9 | This is a Swift SDK built for iOS that manages sessions with SuperTokens. 10 | 11 | Learn more at https://supertokens.com 12 | 13 | ## Documentation 14 | 15 | To see documentation, please click [here](https://supertokens.com/docs/guides). 16 | 17 | ## Contributing 18 | 19 | Please refer to the [CONTRIBUTING.md](https://github.com/supertokens/supertokens-ios/blob/master/CONTRIBUTING.md) file in this repo. 20 | 21 | ## Contact us 22 | 23 | For any queries, or support requests, please email us at team@supertokens.com, or join our [Discord](supertokens.com/discord) server. 24 | 25 | ## Authors 26 | 27 | Created with :heart: by the folks at SuperTokens.com. -------------------------------------------------------------------------------- /testHelpers/testapp/Pods/Target Support Files/SuperTokensIOS/SuperTokensIOS.debug.xcconfig: -------------------------------------------------------------------------------- 1 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO 2 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/SuperTokensIOS 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift 5 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 6 | PODS_BUILD_DIR = ${BUILD_DIR} 7 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 8 | PODS_ROOT = ${SRCROOT} 9 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/../../.. 10 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates 11 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 12 | SKIP_INSTALL = YES 13 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 14 | -------------------------------------------------------------------------------- /testHelpers/testapp/Pods/Target Support Files/SuperTokensIOS/SuperTokensIOS.release.xcconfig: -------------------------------------------------------------------------------- 1 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO 2 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/SuperTokensIOS 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift 5 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 6 | PODS_BUILD_DIR = ${BUILD_DIR} 7 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 8 | PODS_ROOT = ${SRCROOT} 9 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/../../.. 10 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates 11 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 12 | SKIP_INSTALL = YES 13 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 14 | -------------------------------------------------------------------------------- /testHelpers/testapp/Pods/Local Podspecs/SuperTokensIOS.podspec.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "SuperTokensIOS", 3 | "version": "0.0.1", 4 | "summary": "SuperTokens SDK for using login and session management functionality in iOS apps", 5 | "description": "SuperTokens SDK for iOS written in Swift. This SDK manages sessions for you and allows you to build login functionality easily.", 6 | "homepage": "https://github.com/supertokens/supertokens-ios", 7 | "license": { 8 | "type": "Apache 2.0", 9 | "file": "LICENSE.md" 10 | }, 11 | "authors": { 12 | "rishabhpoddar": "rishabh@supertokens.io" 13 | }, 14 | "source": { 15 | "git": "https://github.com/supertokens/supertokens-ios.git", 16 | "tag": "v0.0.1" 17 | }, 18 | "platforms": { 19 | "ios": "13.0" 20 | }, 21 | "source_files": "SuperTokensIOS/Classes/**/*", 22 | "swift_versions": "5.0", 23 | "swift_version": "5.0" 24 | } 25 | -------------------------------------------------------------------------------- /examples/with-thirdparty/backend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "supertokens-node", 3 | "version": "0.0.1", 4 | "private": true, 5 | "description": "", 6 | "main": "index.js", 7 | "scripts": { 8 | "start": "npx ts-node-dev --project ./tsconfig.json ./index.ts" 9 | }, 10 | "dependencies": { 11 | "cors": "^2.8.5", 12 | "express": "^4.18.1", 13 | "helmet": "^5.1.0", 14 | "morgan": "^1.10.0", 15 | "npm-run-all": "^4.1.5", 16 | "supertokens-node": "latest", 17 | "ts-node-dev": "^2.0.0", 18 | "typescript": "^4.7.2" 19 | }, 20 | "devDependencies": { 21 | "@types/cors": "^2.8.12", 22 | "@types/express": "^4.17.17", 23 | "@types/morgan": "^1.9.3", 24 | "@types/node": "^16.11.38", 25 | "nodemon": "^2.0.16" 26 | }, 27 | "keywords": [], 28 | "author": "", 29 | "license": "ISC" 30 | } 31 | -------------------------------------------------------------------------------- /testHelpers/testapp/Pods/Target Support Files/SuperTokensIOS/SuperTokensIOS-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 | 0.0.1 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /testHelpers/testapp/Pods/Target Support Files/Pods-SuperTokensSession_Tests/Pods-SuperTokensSession_Tests-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 | -------------------------------------------------------------------------------- /testHelpers/testapp/Pods/Target Support Files/Pods-SuperTokensSession_Example/Pods-SuperTokensSession_Example-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 | -------------------------------------------------------------------------------- /testHelpers/testapp/Pods/Target Support Files/Pods-SuperTokensSession_Tests/Pods-SuperTokensSession_Tests-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 | Generated by CocoaPods - https://cocoapods.org 18 | Title 19 | 20 | Type 21 | PSGroupSpecifier 22 | 23 | 24 | StringsTable 25 | Acknowledgements 26 | Title 27 | Acknowledgements 28 | 29 | 30 | -------------------------------------------------------------------------------- /.circleci/updateDocsInWebsite: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # get current version---------- 3 | version=`cat ../SuperTokensIOS.podspec | grep -e "s.version =" -e "s.version="` 4 | 5 | while IFS='"' read -ra ADDR; do 6 | counter=0 7 | for i in "${ADDR[@]}"; do 8 | if [ $counter == 1 ] 9 | then 10 | version=$i 11 | fi 12 | counter=$(($counter+1)) 13 | done 14 | done <<< "$version" 15 | 16 | # replace path version with X 17 | IFS='.' read -r -a array <<< "$version" 18 | versionFolder="${array[0]}"."${array[1]}".X 19 | 20 | (cd ../../supertokens-backend-website && mkdir -p ./app/docs/sdk/docs/ios/${versionFolder}) 21 | cp -r ../docs/* ../../supertokens-backend-website/app/docs/sdk/docs/ios/ 22 | cp -r ../docs/* ../../supertokens-backend-website/app/docs/sdk/docs/ios/${versionFolder} 23 | 24 | # push to git 25 | git config --global user.email "$EMAIL" 26 | git config --global user.name "$NAME" 27 | (cd ../../supertokens-backend-website && git add --all && git commit -m"updates ios sdk docs" && git pull && git push && ./releaseDev.sh) -------------------------------------------------------------------------------- /testHelpers/testapp/Pods/Target Support Files/Pods-SuperTokensSession_Example/Pods-SuperTokensSession_Example.debug.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO 3 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/SuperTokensIOS" 4 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 5 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/SuperTokensIOS/SuperTokensIOS.framework/Headers" 6 | LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/Frameworks' '@loader_path/Frameworks' 7 | LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift 8 | OTHER_LDFLAGS = $(inherited) -framework "SuperTokensIOS" 9 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 10 | PODS_BUILD_DIR = ${BUILD_DIR} 11 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 12 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 13 | PODS_ROOT = ${SRCROOT}/Pods 14 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates 15 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 16 | -------------------------------------------------------------------------------- /testHelpers/testapp/Pods/Target Support Files/Pods-SuperTokensSession_Example/Pods-SuperTokensSession_Example.release.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO 3 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/SuperTokensIOS" 4 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 5 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/SuperTokensIOS/SuperTokensIOS.framework/Headers" 6 | LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/Frameworks' '@loader_path/Frameworks' 7 | LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift 8 | OTHER_LDFLAGS = $(inherited) -framework "SuperTokensIOS" 9 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 10 | PODS_BUILD_DIR = ${BUILD_DIR} 11 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 12 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 13 | PODS_ROOT = ${SRCROOT}/Pods 14 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates 15 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 16 | -------------------------------------------------------------------------------- /testHelpers/testapp/SuperTokensSession/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ios-marketing", 45 | "size" : "1024x1024", 46 | "scale" : "1x" 47 | } 48 | ], 49 | "info" : { 50 | "version" : 1, 51 | "author" : "xcode" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /.circleci/markDevTagAsTestNotPassed.sh: -------------------------------------------------------------------------------- 1 | frontendDriverJson=`cat ../frontendDriverInterfaceSupported.json` 2 | frontendDriverLength=`echo $frontendDriverJson | jq ".versions | length"` 3 | frontendDriverArray=`echo $frontendDriverJson | jq ".versions"` 4 | echo "got frontend driver relations" 5 | 6 | # get sdk version 7 | version=`cat ../SuperTokensIOS.podspec | grep -e "s.version =" -e "s.version="` 8 | while IFS='"' read -ra ADDR; do 9 | counter=0 10 | for i in "${ADDR[@]}"; do 11 | if [ $counter == 1 ] 12 | then 13 | version=$i 14 | fi 15 | counter=$(($counter+1)) 16 | done 17 | done <<< "$version" 18 | 19 | responseStatus=`curl -s -o /dev/null -w "%{http_code}" -X PUT \ 20 | https://api.supertokens.io/0/frontend \ 21 | -H 'Content-Type: application/json' \ 22 | -H 'api-version: 0' \ 23 | -d "{ 24 | \"password\": \"$SUPERTOKENS_API_KEY\", 25 | \"version\":\"$version\", 26 | \"name\": \"ios\", 27 | \"frontendDriverInterfaces\": $frontendDriverArray 28 | }"` 29 | if [ $responseStatus -ne "200" ] 30 | then 31 | echo "failed core PUT API status code: $responseStatus. Exiting!" 32 | exit 1 33 | fi -------------------------------------------------------------------------------- /SuperTokensIOS/Classes/Error.swift: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2020, VRAI Labs and/or its affiliates. All rights reserved. 2 | * 3 | * This software is licensed under the Apache License, Version 2.0 (the 4 | * "License") as published by the Apache Software Foundation. 5 | * 6 | * You may not use this file except in compliance with the License. You may 7 | * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | * License for the specific language governing permissions and limitations 13 | * under the License. 14 | */ 15 | 16 | import Foundation 17 | 18 | public enum SuperTokensError: Error { 19 | case initError(message: String) 20 | case apiError(message: String) 21 | case generalError(message: String) 22 | case illegalAccess(message: String) 23 | case maxRetryAttemptsReachedForSessionRefresh(message: String) 24 | } 25 | 26 | internal enum SDKFailableError: Error { 27 | case failableError 28 | } 29 | -------------------------------------------------------------------------------- /.circleci/markAsSuccess.sh: -------------------------------------------------------------------------------- 1 | frontendDriverJson=`cat ../frontendDriverInterfaceSupported.json` 2 | frontendDriverLength=`echo $frontendDriverJson | jq ".versions | length"` 3 | frontendDriverArray=`echo $frontendDriverJson | jq ".versions"` 4 | echo "got frontend driver relations" 5 | 6 | # get sdk version 7 | version=`cat ../SuperTokensIOS.podspec | grep -e "s.version =" -e "s.version="` 8 | while IFS='"' read -ra ADDR; do 9 | counter=0 10 | for i in "${ADDR[@]}"; do 11 | if [ $counter == 1 ] 12 | then 13 | version=$i 14 | fi 15 | counter=$(($counter+1)) 16 | done 17 | done <<< "$version" 18 | 19 | echo "calling /frontend PATCH to make testing passed" 20 | responseStatus=`curl -s -o /dev/null -w "%{http_code}" -X PATCH \ 21 | https://api.supertokens.io/0/frontend \ 22 | -H 'Content-Type: application/json' \ 23 | -H 'api-version: 0' \ 24 | -d "{ 25 | \"password\": \"$SUPERTOKENS_API_KEY\", 26 | \"version\":\"$version\", 27 | \"name\": \"ios\", 28 | \"testPassed\": true 29 | }"` 30 | if [ $responseStatus -ne "200" ] 31 | then 32 | echo "patch api failed" 33 | exit 1 34 | fi -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 5.8 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "SuperTokensIOS", 8 | platforms: [ .macOS(.v12), .iOS(.v13) ], 9 | products: [ 10 | // Products define the executables and libraries a package produces, making them visible to other packages. 11 | .library( 12 | name: "SuperTokensIOS", 13 | targets: ["SuperTokensIOS"]) 14 | ], 15 | dependencies: [], 16 | targets: [ 17 | // Targets are the basic building blocks of a package, defining a module or a test suite. 18 | // Targets can depend on other targets in this package and products from dependencies. 19 | .target( 20 | name: "SuperTokensIOS", 21 | dependencies: [], 22 | path: "SuperTokensIOS/Classes" 23 | ), 24 | .testTarget( 25 | name: "SuperTokensIOSTests", 26 | dependencies: ["SuperTokensIOS"], 27 | path: "testHelpers/testapp/Tests" 28 | ) 29 | ], 30 | swiftLanguageVersions: [.v5] 31 | ) 32 | -------------------------------------------------------------------------------- /examples/with-thirdparty/with-thirdparty/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | GIDServerClientID 6 | GOOGLE_WEB_CLIENT_ID 7 | CFBundleURLTypes 8 | 9 | 10 | CFBundleURLSchemes 11 | 12 | GOOGLE_IOS_URL_SCHEME 13 | 14 | 15 | 16 | GIDClientID 17 | GOOGLE_IOS_CLIENT_ID 18 | UIApplicationSceneManifest 19 | 20 | UIApplicationSupportsMultipleScenes 21 | 22 | UISceneConfigurations 23 | 24 | UIWindowSceneSessionRoleApplication 25 | 26 | 27 | UISceneConfigurationName 28 | Default Configuration 29 | UISceneDelegateClassName 30 | $(PRODUCT_MODULE_NAME).SceneDelegate 31 | UISceneStoryboardFile 32 | Main 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /SuperTokensIOS/Classes/UnauthorisedResponse.swift: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2020, VRAI Labs and/or its affiliates. All rights reserved. 2 | * 3 | * This software is licensed under the Apache License, Version 2.0 (the 4 | * "License") as published by the Apache Software Foundation. 5 | * 6 | * You may not use this file except in compliance with the License. You may 7 | * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | * License for the specific language governing permissions and limitations 13 | * under the License. 14 | */ 15 | import Foundation 16 | 17 | internal class UnauthorisedResponse { 18 | internal enum UnauthorisedStatus { 19 | case SESSION_EXPIRED 20 | case API_ERROR 21 | case RETRY 22 | } 23 | let status: UnauthorisedStatus 24 | let error: Error? 25 | 26 | init(status: UnauthorisedStatus, error: Error? = nil) { 27 | self.status = status 28 | self.error = error 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /SuperTokensIOS/Classes/extensions/String.swift: -------------------------------------------------------------------------------- 1 | // 2 | // String.swift 3 | // SuperTokensSession 4 | // 5 | // Created by Nemi Shah on 30/09/22. 6 | // 7 | 8 | import Foundation 9 | 10 | extension String { 11 | func trim() -> String { 12 | return self.trimmingCharacters(in: .whitespacesAndNewlines) 13 | } 14 | 15 | func matches(regex: String) -> Bool { 16 | return NSPredicate(format: "SELF MATCHES %@", regex).evaluate(with: self) 17 | } 18 | 19 | func indexOf(character: Character) -> String.Index? { 20 | return self.firstIndex(of: character) 21 | } 22 | 23 | func substring(fromIndex: Int) -> String { 24 | if fromIndex >= self.count { 25 | return "" 26 | } 27 | 28 | let startIndex = self.index(self.startIndex, offsetBy: fromIndex) 29 | 30 | return String(self[startIndex.. String { 34 | let startIndex = self.index(self.startIndex, offsetBy: fromIndex) 35 | return String(self[startIndex.. { 26 | let session = req.session; 27 | res.send({ 28 | sessionHandle: session!.getHandle(), 29 | userId: session!.getUserId(), 30 | accessTokenPayload: session!.getAccessTokenPayload(), 31 | }); 32 | }); 33 | 34 | // In case of session related errors, this error handler 35 | // returns 401 to the client. 36 | app.use(errorHandler()); 37 | 38 | app.listen(3001, () => console.log(`API Server listening on port 3001`)); 39 | -------------------------------------------------------------------------------- /testHelpers/testapp/SuperTokensSession/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 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationLandscapeLeft 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationPortrait 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /SuperTokensIOS.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod lib lint SuperTokensIOS.podspec' to ensure this is a 3 | # valid spec before submitting. 4 | # 5 | # Any lines starting with a # are optional, but their use is encouraged 6 | # To learn more about a Podspec see https://guides.cocoapods.org/syntax/podspec.html 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | s.name = 'SuperTokensIOS' 11 | s.version = "0.4.3" 12 | s.summary = 'SuperTokens SDK for using login and session management functionality in iOS apps' 13 | 14 | # This description is used to generate tags and improve search results. 15 | # * Think: What does it do? Why did you write it? What is the focus? 16 | # * Try to keep it short, snappy and to the point. 17 | # * Write the description between the DESC delimiters below. 18 | # * Finally, don't worry about the indent, CocoaPods strips it! 19 | 20 | s.description = <<-DESC 21 | SuperTokens SDK for iOS written in Swift. This SDK manages sessions for you and allows you to build login functionality easily. 22 | DESC 23 | 24 | s.homepage = 'https://github.com/supertokens/supertokens-ios' 25 | s.license = { :type => 'Apache 2.0', :file => 'LICENSE.md' } 26 | s.author = { 'rishabhpoddar' => 'rishabh@supertokens.io' } 27 | s.source = { :git => 'https://github.com/supertokens/supertokens-ios.git', :tag => "v#{s.version}" } 28 | 29 | s.ios.deployment_target = '13.0' 30 | 31 | s.source_files = 'SuperTokensIOS/Classes/**/*' 32 | 33 | s.swift_versions = "5.0" 34 | end 35 | -------------------------------------------------------------------------------- /hooks/pre-commit.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # get current version---------- 3 | version=`cat SuperTokensIOS.podspec | grep -e "s.version =" -e "s.version="` 4 | 5 | while IFS='"' read -ra ADDR; do 6 | counter=0 7 | for i in "${ADDR[@]}"; do 8 | if [ $counter == 1 ] 9 | then 10 | version=$i 11 | fi 12 | counter=$(($counter+1)) 13 | done 14 | done <<< "$version" 15 | 16 | # get version from code 17 | codeversion=`cat SuperTokensIOS/Classes/Version.swift | grep -e "sdkVersion =" -e "sdkVersion="` 18 | while IFS='"' read -ra ADDR; do 19 | counter=0 20 | for i in "${ADDR[@]}"; do 21 | if [ $counter == 1 ] 22 | then 23 | codeversion=$i 24 | fi 25 | counter=$(($counter+1)) 26 | done 27 | done <<< "$codeversion" 28 | 29 | if [ $version != $codeversion ] 30 | then 31 | RED='\033[0;31m' 32 | NC='\033[0m' # No Color 33 | printf "${RED}Version codes in podspec and Version.swift are not the same${NC}\n" 34 | exit 1 35 | fi 36 | 37 | # get git branch name----------- 38 | branch_name="$(git symbolic-ref HEAD 2>/dev/null)" || 39 | branch_name="(unnamed branch)" # detached HEAD 40 | 41 | branch_name=${branch_name##refs/heads/} 42 | 43 | # check if branch is correct based on the version----------- 44 | if [ $branch_name == "master" ] 45 | then 46 | YELLOW='\033[1;33m' 47 | NC='\033[0m' # No Color 48 | printf "${YELLOW}committing to MASTER${NC}\n" 49 | exit 0 50 | elif [[ $version == $branch_name* ]] 51 | then 52 | continue=1 53 | else 54 | YELLOW='\033[1;33m' 55 | NC='\033[0m' # No Color 56 | printf "${YELLOW}Not committing to master or version branches${NC}\n" 57 | fi 58 | -------------------------------------------------------------------------------- /testHelpers/server/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Build generated 2 | build/ 3 | !testHelpers/server/build 4 | DerivedData/ 5 | 6 | ## Various settings 7 | *.pbxuser 8 | !default.pbxuser 9 | *.mode1v3 10 | !default.mode1v3 11 | *.mode2v3 12 | !default.mode2v3 13 | *.perspectivev3 14 | !default.perspectivev3 15 | xcuserdata/ 16 | 17 | ## Other 18 | *.moved-aside 19 | *.xccheckout 20 | *.xcscmblueprint 21 | 22 | ## Obj-C/Swift specific 23 | *.hmap 24 | *.ipa 25 | *.dSYM.zip 26 | *.dSYM 27 | 28 | ## Backend for testing 29 | node_modules 30 | 31 | apiPassword 32 | releasePassword 33 | .DS_Store 34 | 35 | # OS X 36 | .DS_Store 37 | 38 | # Xcode 39 | build/ 40 | *.pbxuser 41 | !default.pbxuser 42 | *.mode1v3 43 | !default.mode1v3 44 | *.mode2v3 45 | !default.mode2v3 46 | *.perspectivev3 47 | !default.perspectivev3 48 | xcuserdata/ 49 | *.xccheckout 50 | profile 51 | *.moved-aside 52 | DerivedData 53 | *.hmap 54 | *.ipa 55 | 56 | # Bundler 57 | .bundle 58 | 59 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 60 | # Carthage/Checkouts 61 | 62 | Carthage/Build 63 | 64 | # We recommend against adding the Pods directory to your .gitignore. However 65 | # you should judge for yourself, the pros and cons are mentioned at: 66 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 67 | # 68 | # Note: if you ignore the Pods directory, make sure to uncomment 69 | # `pod install` in .travis.yml 70 | # 71 | # Pods/ 72 | 73 | #Swift package Manager 74 | .build 75 | DerivedData 76 | /.previous-build 77 | xcuserdata 78 | .DS_Store 79 | *~ 80 | \#* 81 | .\#* 82 | .*.sw[nop] 83 | *.xcscmblueprint 84 | /default.profraw 85 | *.xcodeproj 86 | Utilities/Docker/*.tar.gz 87 | .swiftpm 88 | Package.resolved 89 | /build 90 | *.pyc 91 | .docc-build 92 | .vscode 93 | -------------------------------------------------------------------------------- /examples/with-thirdparty/with-thirdparty/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 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | 3 | # this allows you to use CircleCI's dynamic configuration feature 4 | setup: true 5 | 6 | orbs: 7 | continuation: circleci/continuation@0.1.2 8 | slack: circleci/slack@3.4.2 9 | jq: circleci/jq@2.2.0 10 | jobs: 11 | publish: 12 | macos: 13 | xcode: 14.1.0 14 | steps: 15 | - checkout 16 | - run: pod lib lint SuperTokensIOS.podspec 17 | - run: pod trunk push 18 | - slack/status 19 | test: 20 | macos: 21 | xcode: 14.1.0 22 | steps: 23 | - checkout 24 | - run: 25 | name: Generate config 26 | command: cd .circleci && ./generateConfig.sh 27 | - continuation/continue: 28 | configuration_path: .circleci/config_continue.yml 29 | update-docs: 30 | macos: 31 | xcode: 14.3.1 32 | steps: 33 | - checkout 34 | - run: cd ../ && git clone git@github.com:supertokens/supertokens-backend-website.git 35 | - run: gem install jazzy 36 | - run: jazzy --output ./docs --podspec SuperTokensIOS.podspec 37 | - run: (cd .circleci && ./updateDocsInWebsite) 38 | - slack/status 39 | 40 | workflows: 41 | version: 2 42 | tagged-build: 43 | jobs: 44 | - publish: 45 | requires: 46 | - update-docs 47 | context: 48 | - slack-notification 49 | filters: 50 | tags: 51 | only: /v[0-9]+(\.[0-9]+)*/ 52 | branches: 53 | ignore: /.*/ 54 | - test: 55 | context: 56 | - slack-notification 57 | filters: 58 | tags: 59 | only: /dev-v[0-9]+(\.[0-9]+)*/ 60 | branches: 61 | only: /test-cicd\/.*/ 62 | - update-docs: 63 | context: 64 | - slack-notification 65 | filters: 66 | tags: 67 | only: /v[0-9]+(\.[0-9]+)*/ 68 | branches: 69 | ignore: /.*/ -------------------------------------------------------------------------------- /testHelpers/testapp/SuperTokensSession/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 | -------------------------------------------------------------------------------- /examples/with-thirdparty/with-thirdparty/LoginScreen/GithubLoginURLProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GithubLoginURLProtocol.swift 3 | // with-thirdparty 4 | // 5 | // Created by Nemi Shah on 13/11/23. 6 | // 7 | 8 | import Foundation 9 | 10 | public class GithubLoginProtocol: URLProtocol { 11 | public override class func canInit(with request: URLRequest) -> Bool { 12 | if let url: String = request.url?.absoluteString, url == "https://github.com/login/oauth/access_token" { 13 | return true 14 | } 15 | 16 | return false 17 | } 18 | 19 | public override class func canonicalRequest(for request: URLRequest) -> URLRequest { 20 | return request 21 | } 22 | 23 | public override func startLoading() { 24 | var mutableRequest = (self.request as NSURLRequest).mutableCopy() as! NSMutableURLRequest 25 | mutableRequest.setValue("application/json", forHTTPHeaderField: "Accept") 26 | let customSession = URLSession(configuration: URLSessionConfiguration.default) 27 | 28 | let apiRequest = mutableRequest.copy() as! URLRequest 29 | customSession.dataTask(with: apiRequest) { 30 | data, response, error in 31 | 32 | self.resolveToUser(data: data, response: response, error: error) 33 | }.resume() 34 | } 35 | 36 | public override func stopLoading() { 37 | // do nothing 38 | } 39 | 40 | func resolveToUser(data: Data?, response: URLResponse?, error: Error?) { 41 | // This will call the appropriate callbacks and return the data back to the user 42 | if error != nil { 43 | self.client?.urlProtocol(self, didFailWithError: error!) 44 | } 45 | 46 | if data != nil { 47 | self.client?.urlProtocol(self, didLoad: data!) 48 | } 49 | 50 | if response != nil { 51 | self.client?.urlProtocol(self, didReceive: response!, cacheStoragePolicy: .notAllowed) 52 | } 53 | 54 | // After everything, we need to call this to indicate to URLSession that this protocol has finished its task 55 | self.client?.urlProtocolDidFinishLoading(self) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /examples/with-thirdparty/with-thirdparty/HomeScreen/HomeScreenViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HomeScreenViewController.swift 3 | // with-thirdparty 4 | // 5 | // Created by Nemi Shah on 10/11/23. 6 | // 7 | 8 | import UIKit 9 | import SuperTokensIOS 10 | 11 | class HomeScreenViewController: UIViewController { 12 | @IBOutlet var contentContainer: UIView! 13 | @IBOutlet var userIdContainer: UIView! 14 | @IBOutlet var userId: UILabel! 15 | @IBOutlet var resultView: UIView! 16 | @IBOutlet var resultTextField: UILabel! 17 | 18 | override func viewDidAppear(_ animated: Bool) { 19 | super.viewDidAppear(animated) 20 | 21 | contentContainer.layer.cornerRadius = 16 22 | contentContainer.clipsToBounds = true 23 | 24 | resultView.layer.cornerRadius = 16 25 | resultView.clipsToBounds = true 26 | 27 | do { 28 | let _userId = try SuperTokens.getUserId() 29 | userId.text = _userId 30 | } catch {} 31 | 32 | userIdContainer.layer.borderWidth = 1 33 | userIdContainer.layer.borderColor = UIColor(red: 255/255, green: 63/255, blue: 51/255, alpha: 1).cgColor 34 | userIdContainer.layer.cornerRadius = 8 35 | 36 | resultView.isHidden = true 37 | } 38 | 39 | @IBAction func callAPI() { 40 | resultView.isHidden = true 41 | var request = URLRequest(url: URL(string: Constants.apiDomain + "/sessioninfo")!) 42 | 43 | URLSession.shared.dataTask(with: request) { 44 | data, response, error in 45 | 46 | if data != nil { 47 | if let dataString: String = String(data: data!, encoding: .utf8) { 48 | DispatchQueue.main.async { [weak self] in 49 | self?.resultTextField.text = dataString 50 | self?.resultView.isHidden = false 51 | } 52 | } 53 | } 54 | }.resume() 55 | } 56 | 57 | @IBAction func signOut() { 58 | SuperTokens.signOut(completionHandler: { _ in 59 | DispatchQueue.main.async { [weak self] in 60 | self?.navigationController?.popViewController(animated: true) 61 | } 62 | }) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /testHelpers/testapp/SuperTokensSession/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // SuperTokensSession 4 | // 5 | // Created by rishabhpoddar on 03/24/2020. 6 | // Copyright (c) 2020 rishabhpoddar. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /examples/with-thirdparty/with-thirdparty/SceneDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.swift 3 | // with-thirdparty 4 | // 5 | // Created by Nemi Shah on 10/11/23. 6 | // 7 | 8 | import UIKit 9 | 10 | class SceneDelegate: UIResponder, UIWindowSceneDelegate { 11 | 12 | var window: UIWindow? 13 | 14 | 15 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { 16 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. 17 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. 18 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). 19 | guard let _ = (scene as? UIWindowScene) else { return } 20 | } 21 | 22 | func sceneDidDisconnect(_ scene: UIScene) { 23 | // Called as the scene is being released by the system. 24 | // This occurs shortly after the scene enters the background, or when its session is discarded. 25 | // Release any resources associated with this scene that can be re-created the next time the scene connects. 26 | // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead). 27 | } 28 | 29 | func sceneDidBecomeActive(_ scene: UIScene) { 30 | // Called when the scene has moved from an inactive state to an active state. 31 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. 32 | } 33 | 34 | func sceneWillResignActive(_ scene: UIScene) { 35 | // Called when the scene will move from an active state to an inactive state. 36 | // This may occur due to temporary interruptions (ex. an incoming phone call). 37 | } 38 | 39 | func sceneWillEnterForeground(_ scene: UIScene) { 40 | // Called as the scene transitions from the background to the foreground. 41 | // Use this method to undo the changes made on entering the background. 42 | } 43 | 44 | func sceneDidEnterBackground(_ scene: UIScene) { 45 | // Called as the scene transitions from the foreground to the background. 46 | // Use this method to save data, release shared resources, and store enough scene-specific state information 47 | // to restore the scene back to its current state. 48 | } 49 | 50 | 51 | } 52 | 53 | -------------------------------------------------------------------------------- /examples/with-thirdparty/with-thirdparty/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // with-thirdparty 4 | // 5 | // Created by Nemi Shah on 10/11/23. 6 | // 7 | 8 | import UIKit 9 | import SuperTokensIOS 10 | import GoogleSignIn 11 | import AppAuth 12 | 13 | @main 14 | class AppDelegate: UIResponder, UIApplicationDelegate { 15 | var currentAuthorizationFlow: OIDExternalUserAgentSession? 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | 20 | do { 21 | try SuperTokens.initialize(apiDomain: Constants.apiDomain) 22 | } catch { 23 | print("Failed to initialise SuperTokens: " + error.localizedDescription) 24 | } 25 | 26 | URLProtocol.registerClass(SuperTokensURLProtocol.self) 27 | URLProtocol.registerClass(GithubLoginProtocol.self) 28 | 29 | return true 30 | } 31 | 32 | // MARK: UISceneSession Lifecycle 33 | 34 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { 35 | // Called when a new scene session is being created. 36 | // Use this method to select a configuration to create the new scene with. 37 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) 38 | } 39 | 40 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { 41 | // Called when the user discards a scene session. 42 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. 43 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return. 44 | } 45 | 46 | func application( 47 | _ app: UIApplication, 48 | open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:] 49 | ) -> Bool { 50 | var handled: Bool 51 | 52 | handled = GIDSignIn.sharedInstance.handle(url) 53 | if handled { 54 | return true 55 | } 56 | 57 | if let authorizationFlow = self.currentAuthorizationFlow, authorizationFlow.resumeExternalUserAgentFlow(with: url) { 58 | self.currentAuthorizationFlow = nil 59 | return true 60 | } 61 | 62 | // Handle other custom URL types. 63 | 64 | // If not handled by this app, return false. 65 | return false 66 | } 67 | } 68 | 69 | -------------------------------------------------------------------------------- /.circleci/doTests.sh: -------------------------------------------------------------------------------- 1 | echo "Starting tests for FDI $1"; 2 | 3 | JAVA_HOME=/Users/distiller/jdk-15.0.1.jdk/Contents/Home/ 4 | export JAVA_HOME 5 | 6 | if [ -z "$SUPERTOKENS_API_KEY" ]; then 7 | echo "SUPERTOKENS_API_KEY not set" 8 | exit 1 9 | fi 10 | frontendDriverVersion=$1 11 | 12 | driverVersionXY=`curl -s -X GET \ 13 | "https://api.supertokens.io/0/frontend-driver-interface/dependency/driver/latest?password=$SUPERTOKENS_API_KEY&mode=DEV&version=$frontendDriverVersion&driverName=node" \ 14 | -H 'api-version: 0'` 15 | if [[ `echo $driverVersionXY | jq .driver` == "null" ]] 16 | then 17 | echo "fetching latest X.Y version for driver given frontend-driver-interface X.Y version: $frontendDriverVersion gave response: $driverVersionXY. Please make sure all relevant drivers have been pushed." 18 | exit 1 19 | fi 20 | driverVersionXY=$(echo $driverVersionXY | jq .driver | tr -d '"') 21 | 22 | driverInfo=`curl -s -X GET \ 23 | "https://api.supertokens.io/0/driver/latest?password=$SUPERTOKENS_API_KEY&mode=DEV&version=$driverVersionXY&name=node" \ 24 | -H 'api-version: 0'` 25 | if [[ `echo $driverInfo | jq .tag` == "null" ]] 26 | then 27 | echo "fetching latest X.Y.Z version for driver, X.Y version: $driverVersionXY gave response: $driverInfo" 28 | exit 1 29 | fi 30 | driverTag=$(echo $driverInfo | jq .tag | tr -d '"') 31 | driverVersion=$(echo $driverInfo | jq .version | tr -d '"') 32 | 33 | git clone git@github.com:supertokens/supertokens-node.git 34 | cd supertokens-node 35 | git checkout $driverTag 36 | coreDriverJson=`cat ./coreDriverInterfaceSupported.json` 37 | coreDriverLength=`echo $coreDriverJson | jq ".versions | length"` 38 | coreDriverArray=`echo $coreDriverJson | jq ".versions"` 39 | coreDriverVersion=`echo $coreDriverArray | jq ". | last"` 40 | coreDriverVersion=`echo $coreDriverVersion | tr -d '"'` 41 | cd ../ 42 | rm -rf supertokens-node 43 | 44 | coreFree=`curl -s -X GET \ 45 | "https://api.supertokens.io/0/core-driver-interface/dependency/core/latest?password=$SUPERTOKENS_API_KEY&planType=FREE&mode=DEV&version=$coreDriverVersion" \ 46 | -H 'api-version: 0'` 47 | if [[ `echo $coreFree | jq .core` == "null" ]] 48 | then 49 | echo "fetching latest X.Y version for core given core-driver-interface X.Y version: $coreDriverVersion, planType: FREE gave response: $coreFree. Please make sure all relevant cores have been pushed." 50 | exit 1 51 | fi 52 | coreFree=$(echo $coreFree | jq .core | tr -d '"') 53 | 54 | ./setupAndTestWithFreeCore.sh $coreFree $driverTag 55 | if [[ $? -ne 0 ]] 56 | then 57 | echo "test failed... exiting!" 58 | exit 1 59 | fi 60 | rm -rf ../../supertokens-root 61 | rm -rf ../testHelpers/server/node_modules/supertokens-node 62 | git checkout HEAD -- ../testHelpers/server/package.json -------------------------------------------------------------------------------- /.circleci/setupAndTestWithFreeCore.sh: -------------------------------------------------------------------------------- 1 | coreInfo=`curl -s -X GET \ 2 | "https://api.supertokens.io/0/core/latest?password=$SUPERTOKENS_API_KEY&planType=FREE&mode=DEV&version=$1" \ 3 | -H 'api-version: 0'` 4 | if [[ `echo $coreInfo | jq .tag` == "null" ]] 5 | then 6 | echo "fetching latest X.Y.Z version for core, X.Y version: $1, planType: FREE gave response: $coreInfo" 7 | exit 1 8 | fi 9 | coreTag=$(echo $coreInfo | jq .tag | tr -d '"') 10 | coreVersion=$(echo $coreInfo | jq .version | tr -d '"') 11 | 12 | pluginInterfaceVersionXY=`curl -s -X GET \ 13 | "https://api.supertokens.io/0/core/dependency/plugin-interface/latest?password=$SUPERTOKENS_API_KEY&planType=FREE&mode=DEV&version=$1" \ 14 | -H 'api-version: 0'` 15 | if [[ `echo $pluginInterfaceVersionXY | jq .pluginInterface` == "null" ]] 16 | then 17 | echo "fetching latest X.Y version for plugin-interface, given core X.Y version: $1, planType: FREE gave response: $pluginInterfaceVersionXY" 18 | exit 1 19 | fi 20 | pluginInterfaceVersionXY=$(echo $pluginInterfaceVersionXY | jq .pluginInterface | tr -d '"') 21 | 22 | pluginInterfaceInfo=`curl -s -X GET \ 23 | "https://api.supertokens.io/0/plugin-interface/latest?password=$SUPERTOKENS_API_KEY&planType=FREE&mode=DEV&version=$pluginInterfaceVersionXY" \ 24 | -H 'api-version: 0'` 25 | if [[ `echo $pluginInterfaceInfo | jq .tag` == "null" ]] 26 | then 27 | echo "fetching latest X.Y.Z version for plugin-interface, X.Y version: $pluginInterfaceVersionXY, planType: FREE gave response: $pluginInterfaceInfo" 28 | exit 1 29 | fi 30 | pluginInterfaceTag=$(echo $pluginInterfaceInfo | jq .tag | tr -d '"') 31 | pluginInterfaceVersion=$(echo $pluginInterfaceInfo | jq .version | tr -d '"') 32 | 33 | echo "Testing with node driver: $2, FREE core: $coreVersion, plugin-interface: $pluginInterfaceVersion" 34 | 35 | cd ../../ 36 | git clone git@github.com:supertokens/supertokens-root.git 37 | cd supertokens-root 38 | echo -e "core,$1\nplugin-interface,$pluginInterfaceVersionXY" > modules.txt 39 | ./loadModules --ssh 40 | cd supertokens-core 41 | git checkout $coreTag 42 | cd ../supertokens-plugin-interface 43 | git checkout $pluginInterfaceTag 44 | cd ../ 45 | echo $SUPERTOKENS_API_KEY > apiPassword 46 | ./utils/setupTestEnvLocal 47 | cd ../project/testHelpers/server/ 48 | npm i -d --force 49 | npm i git+https://github.com:supertokens/supertokens-node.git#$2 50 | TEST_MODE=testing INSTALL_PATH=../../../supertokens-root node . & 51 | pid=$! 52 | cd ../../ 53 | xcodebuild test -enableCodeCoverage YES -workspace testHelpers/testapp/SuperTokensSession.xcworkspace -scheme SuperTokensSession-Example -sdk iphonesimulator -destination 'platform=iOS Simulator,OS=16.1,name=iPhone 14 Pro' ONLY_ACTIVE_ARCH=NO 54 | if [[ $? -ne 0 ]] 55 | then 56 | echo "test failed... exiting!" 57 | exit 1 58 | fi 59 | kill -9 $pid -------------------------------------------------------------------------------- /SuperTokensIOS/Classes/NormalisedURLDomain.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NormalisedURLDomain.swift 3 | // SuperTokensSession 4 | // 5 | // Created by Nemi Shah on 30/09/22. 6 | // 7 | 8 | import Foundation 9 | 10 | class NormalisedURLDomain { 11 | private let value: String 12 | 13 | init(url: String) throws { 14 | self.value = try NormalisedURLDomain.normaliseUrlDomainOrThrowError(input: url) 15 | } 16 | 17 | func getAsStringDangerous() -> String { 18 | return self.value 19 | } 20 | 21 | static func normaliseUrlDomainOrThrowError(input: String, ignoreProtocol: Bool = false) throws -> String { 22 | var trimmedInput: String = input.trim() 23 | 24 | do { 25 | if !trimmedInput.starts(with: "http://") && !trimmedInput.starts(with: "https://") { 26 | throw SDKFailableError.failableError 27 | } 28 | 29 | guard let url: URL = URL(string: trimmedInput), let hostName: String = url.host, let scheme: String = url.scheme else { 30 | throw SDKFailableError.failableError 31 | } 32 | 33 | let hostSuffix = url.port == nil ? hostName : hostName + ":\(url.port!)" 34 | 35 | if ignoreProtocol { 36 | if hostName.starts(with: "localhost") || Utils.isIpAddress(input: hostName) { 37 | trimmedInput = "http://" + hostSuffix 38 | } else { 39 | trimmedInput = "https://" + hostSuffix 40 | } 41 | } else { 42 | trimmedInput = scheme + "://" + hostSuffix 43 | } 44 | 45 | return trimmedInput 46 | } catch {} 47 | 48 | if trimmedInput.starts(with: "/") { 49 | throw SuperTokensError.initError(message: "Please provide a valid domain name") 50 | } 51 | 52 | // not a valid URL 53 | if trimmedInput.indexOf(character: ".") == trimmedInput.startIndex { 54 | trimmedInput = trimmedInput.substring(fromIndex: 1) 55 | } 56 | 57 | // If the input contains a . it means they have given a domain name. 58 | // So we try assuming that they have given a domain name 59 | if (trimmedInput.indexOf(character: ".") != nil || trimmedInput.starts(with: "localhost")) && !trimmedInput.starts(with: "http://") && !trimmedInput.starts(with: "https://") { 60 | trimmedInput = "https://" + trimmedInput 61 | 62 | do { 63 | guard let _: URL = URL(string: trimmedInput) else { 64 | throw SDKFailableError.failableError 65 | } 66 | 67 | return try normaliseUrlDomainOrThrowError(input: trimmedInput, ignoreProtocol: true) 68 | } catch {} 69 | } 70 | 71 | throw SuperTokensError.initError(message: "Please provide a valid domain name") 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /examples/with-thirdparty/backend/config.ts: -------------------------------------------------------------------------------- 1 | import ThirdParty from "supertokens-node/recipe/thirdparty"; 2 | import Session from "supertokens-node/recipe/session"; 3 | import { TypeInput } from "supertokens-node/types"; 4 | import Dashboard from "supertokens-node/recipe/dashboard"; 5 | 6 | export const SuperTokensConfig: TypeInput = { 7 | supertokens: { 8 | // this is the location of the SuperTokens core. 9 | connectionURI: "https://try.supertokens.com", 10 | }, 11 | appInfo: { 12 | appName: "SuperTokens Demo App", 13 | apiDomain: "http://192.168.29.87:3001", 14 | websiteDomain: "http://localhost:3000", // this value does not matter for the android app 15 | }, 16 | // recipeList contains all the modules that you want to 17 | // use from SuperTokens. See the full list here: https://supertokens.com/docs/guides 18 | recipeList: [ 19 | ThirdParty.init({ 20 | signInAndUpFeature: { 21 | providers: [ 22 | // We have provided you with development keys which you can use for testing. 23 | // IMPORTANT: Please replace them with your own OAuth keys for production use. 24 | { 25 | config: { 26 | thirdPartyId: "google", 27 | clients: [ 28 | { 29 | clientId: 30 | "GOOGLE_WEB_CLIENT_ID", 31 | clientSecret: "GOOGLE_WEB_CLIENT_SECRET", 32 | }, 33 | ], 34 | }, 35 | }, 36 | { 37 | config: { 38 | thirdPartyId: "github", 39 | clients: [ 40 | { 41 | clientId: "GITHUB_CLIENT_ID", 42 | clientSecret: "GITHUB_CLIENT_SECRET", 43 | }, 44 | ], 45 | }, 46 | }, 47 | { 48 | config: { 49 | thirdPartyId: "apple", 50 | clients: [{ 51 | clientId: "APPLE_CLIENT_ID", 52 | additionalConfig: { 53 | keyId: "APPLE_KEY_ID", 54 | privateKey: 55 | "APPLE_PRIVATE_KEY", 56 | teamId: "APPLE_TEAM_ID", 57 | } 58 | }] 59 | }, 60 | } 61 | ], 62 | }, 63 | }), 64 | Session.init(), 65 | Dashboard.init(), 66 | ], 67 | }; 68 | -------------------------------------------------------------------------------- /.circleci/config_continue.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | orbs: 3 | continuation: circleci/continuation@0.1.2 4 | slack: circleci/slack@3.4.2 5 | jq: circleci/jq@2.2.0 6 | jobs: 7 | test-dev-tag-as-not-passed: 8 | docker: 9 | - image: rishabhpoddar/supertokens_website_sdk_testing_node_16 10 | steps: 11 | - run: echo "Testing branch << pipeline.git.branch >>" 12 | - when: 13 | condition: 14 | not: 15 | matches: 16 | pattern: "^test-cicd/.*$" 17 | value: << pipeline.git.branch >> 18 | steps: 19 | - run: git config --global url."https://github.com/".insteadOf ssh://git@github.com/ # This makes npm use http instead of ssh (required for node 16) 20 | - checkout 21 | - run: (cd .circleci/ && ./markDevTagAsTestNotPassed.sh) 22 | test-unit: 23 | macos: 24 | xcode: 14.1.0 25 | parameters: 26 | fdi-version: 27 | type: string 28 | steps: 29 | - run: 30 | name: Install Rosetta translation environment on Apple Silicon Mac 31 | command: softwareupdate --install-rosetta --agree-to-license 32 | - run: git config --global url."https://github.com/".insteadOf ssh://git@github.com/ # This makes npm use http instead of ssh (required for node 16) 33 | - checkout 34 | - run: cd ../ && curl -L -o java.tar.gz "https://drive.usercontent.google.com/download?id=1zFjmXJFYEYw1bhPIZ0H2Q3oSy_HRCEQk&export=download&authuser=0&confirm=t&uuid=9563b3be-1e99-4293-a38c-001ea7cb2c37&at=APZUnTVgTPkWenZvgghQeFST-Yxk%3A1710776389213" 35 | - run: cd ../ && tar -xzvf java.tar.gz 36 | - run: (cd .circleci/ && ./doTests.sh << parameters.fdi-version >>) 37 | - store_artifacts: 38 | path: ../supertokens-root/logs 39 | destination: logfiles 40 | test-success: 41 | docker: 42 | - image: rishabhpoddar/supertokens_website_sdk_testing_node_16 43 | steps: 44 | - run: git config --global url."https://github.com/".insteadOf ssh://git@github.com/ # This makes npm use http instead of ssh (required for node 16) 45 | - checkout 46 | - run: (cd .circleci/ && ./markAsSuccess.sh) 47 | - slack/status 48 | 49 | workflows: 50 | version: 2 51 | tagged-build: 52 | jobs: 53 | - test-dev-tag-as-not-passed: 54 | filters: 55 | tags: 56 | only: /dev-v[0-9]+(\.[0-9]+)*/ 57 | branches: 58 | only: /test-cicd\/.*/ 59 | - test-unit: 60 | requires: 61 | - test-dev-tag-as-not-passed 62 | context: 63 | - slack-notification 64 | filters: 65 | tags: 66 | only: /dev-v[0-9]+(\.[0-9]+)*/ 67 | branches: 68 | only: /test-cicd\/.*/ 69 | matrix: 70 | parameters: 71 | fdi-version: placeholder 72 | - test-success: 73 | requires: 74 | - test-unit 75 | context: 76 | - slack-notification 77 | filters: 78 | tags: 79 | only: /dev-v[0-9]+(\.[0-9]+)*/ 80 | branches: 81 | ignore: /.*/ -------------------------------------------------------------------------------- /addDevTag: -------------------------------------------------------------------------------- 1 | # check if we need to merge master into this branch------------ 2 | if [[ $(git log origin/master ^HEAD) ]]; then 3 | echo "You need to merge master into this branch. Exiting" 4 | exit 1 5 | fi 6 | 7 | # get version------------ 8 | version=`cat SuperTokensIOS.podspec | grep -e "s.version =" -e "s.version="` 9 | 10 | while IFS='"' read -ra ADDR; do 11 | counter=0 12 | for i in "${ADDR[@]}"; do 13 | if [ $counter == 1 ] 14 | then 15 | version=$i 16 | fi 17 | counter=$(($counter+1)) 18 | done 19 | done <<< "$version" 20 | 21 | codeversion=`cat SuperTokensIOS/Classes/Version.swift | grep -e "sdkVersion =" -e "sdkVersion="` 22 | while IFS='"' read -ra ADDR; do 23 | counter=0 24 | for i in "${ADDR[@]}"; do 25 | if [ $counter == 1 ] 26 | then 27 | codeversion=$i 28 | fi 29 | counter=$(($counter+1)) 30 | done 31 | done <<< "$codeversion" 32 | 33 | # check if podspec and Version.swift versions are similar.----------- 34 | if ! [[ $version == $codeversion ]] 35 | then 36 | RED='\033[0;31m' 37 | NC='\033[0m' # No Color 38 | printf "${RED}Difference between podspec version and Version.swift. Stopping process${NC}\n" 39 | exit 1 40 | fi 41 | 42 | # get current branch name 43 | branch_name="$(git symbolic-ref HEAD 2>/dev/null)" || 44 | branch_name="(unnamed branch)" # detached HEAD 45 | branch_name=${branch_name##refs/heads/} 46 | 47 | # check if branch is correct based on the version----------- 48 | if ! [[ $version == $branch_name* ]] 49 | then 50 | RED='\033[0;31m' 51 | NC='\033[0m' # No Color 52 | printf "${RED}Adding tag to wrong branch. Stopping process${NC}\n" 53 | exit 1 54 | fi 55 | 56 | #Sync tags with remote 57 | git fetch --prune --prune-tags 58 | 59 | # GET Current Commit Hash ------- 60 | if [ $# -eq 0 ] 61 | then 62 | commit_hash=`git log --pretty=format:'%H' -n 1` 63 | else 64 | commit_hash=$1 65 | fi 66 | 67 | # check if current commit already has a tag or not------------ 68 | if [[ `git tag -l --points-at $commit_hash` == "" ]] 69 | then 70 | continue=1 71 | else 72 | RED='\033[0;31m' 73 | NC='\033[0m' 74 | printf "${RED}This commit already has a tag. Please remove that and re-run this script${NC}\n" 75 | echo "git tag --delete " 76 | echo "git push --delete origin " 77 | exit 1 78 | fi 79 | 80 | # check if release version of this tag exists------------ 81 | 82 | if git rev-parse v$version >/dev/null 2>&1 83 | then 84 | RED='\033[0;31m' 85 | NC='\033[0m' 86 | printf "${RED}The released version of this tag already exists${NC}\n" 87 | exit 1 88 | fi 89 | 90 | # add an empty commit if the user has not given a commit hash so that we are sure it's built------------ 91 | if [ $# -eq 0 ] 92 | then 93 | # npm run build-docs TODO: Implement 94 | git add --all 95 | git commit --allow-empty -m"adding dev-v$version tag to this commit to ensure building" 96 | git push 97 | commit_hash=`git log --pretty=format:'%H' -n 1` 98 | fi 99 | 100 | 101 | git tag dev-v$version $commit_hash 102 | git push --tags -------------------------------------------------------------------------------- /SuperTokensIOS/Classes/AntiCSRF.swift: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2020, VRAI Labs and/or its affiliates. All rights reserved. 2 | * 3 | * This software is licensed under the Apache License, Version 2.0 (the 4 | * "License") as published by the Apache Software Foundation. 5 | * 6 | * You may not use this file except in compliance with the License. You may 7 | * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | * License for the specific language governing permissions and limitations 13 | * under the License. 14 | */ 15 | 16 | import Foundation 17 | 18 | // TODO: verify about locking 19 | internal class AntiCSRF { 20 | class AntiCSRFInfo { 21 | var antiCSRF: String? = nil 22 | var associatedAccessTokenUpdate: String? = nil 23 | 24 | init(antiCSRFToken: String, associatedAccessTokenUpdate: String) { 25 | antiCSRF = antiCSRFToken 26 | self.associatedAccessTokenUpdate = associatedAccessTokenUpdate 27 | } 28 | } 29 | 30 | private static var antiCSRFInfo: AntiCSRFInfo? = nil 31 | private static let antiCSRFUserDefaultsKey = "supertokens-ios-anticsrf-key" 32 | 33 | internal static func getToken(associatedAccessTokenUpdate: String?) -> String? { 34 | if associatedAccessTokenUpdate == nil { 35 | AntiCSRF.antiCSRFInfo = nil 36 | return nil 37 | } 38 | 39 | if AntiCSRF.antiCSRFInfo == nil { 40 | let userDefaults = Utils.getUserDefaults() 41 | let antiCSRFToken = userDefaults.string(forKey: AntiCSRF.antiCSRFUserDefaultsKey) 42 | if ( antiCSRFToken == nil ) { 43 | return nil 44 | } 45 | 46 | AntiCSRF.antiCSRFInfo = AntiCSRFInfo(antiCSRFToken: antiCSRFToken!, associatedAccessTokenUpdate: associatedAccessTokenUpdate!) 47 | } else if AntiCSRF.antiCSRFInfo?.associatedAccessTokenUpdate != nil && AntiCSRF.antiCSRFInfo?.associatedAccessTokenUpdate != associatedAccessTokenUpdate! { 48 | AntiCSRF.antiCSRFInfo = nil 49 | return AntiCSRF.getToken(associatedAccessTokenUpdate: associatedAccessTokenUpdate) 50 | } 51 | 52 | return AntiCSRF.antiCSRFInfo!.antiCSRF 53 | } 54 | 55 | internal static func setToken(antiCSRFToken: String, associatedAccessTokenUpdate: String? = nil) { 56 | if associatedAccessTokenUpdate == nil { 57 | AntiCSRF.antiCSRFInfo = nil 58 | return; 59 | } 60 | 61 | let userDefaults = Utils.getUserDefaults() 62 | userDefaults.set(antiCSRFToken, forKey: AntiCSRF.antiCSRFUserDefaultsKey) 63 | userDefaults.synchronize() 64 | 65 | AntiCSRF.antiCSRFInfo = AntiCSRFInfo(antiCSRFToken: antiCSRFToken, associatedAccessTokenUpdate: associatedAccessTokenUpdate!) 66 | } 67 | 68 | internal static func removeToken() { 69 | let userDefaults = Utils.getUserDefaults() 70 | userDefaults.removeObject(forKey: AntiCSRF.antiCSRFUserDefaultsKey) 71 | userDefaults.synchronize() 72 | AntiCSRF.antiCSRFInfo = nil 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies within all project spaces, and it also applies when 49 | an individual is representing the project or its community in public spaces. 50 | Examples of representing a project or community include using an official 51 | project e-mail address, posting via an official social media account, or acting 52 | as an appointed representative at an online or offline event. Representation of 53 | a project may be further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at team@supertokens.io. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | -------------------------------------------------------------------------------- /SuperTokensIOS/Classes/NormalisedURLPath.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NormalisedURLPath.swift 3 | // SuperTokensSession 4 | // 5 | // Created by Nemi Shah on 30/09/22. 6 | // 7 | 8 | import Foundation 9 | 10 | class NormalisedURLPath { 11 | private let value: String 12 | 13 | init(input: String) throws { 14 | self.value = try NormalisedURLPath.normaliseURLPathOrThrowError(input: input) 15 | } 16 | 17 | func startsWith(other: NormalisedURLPath) -> Bool { 18 | return self.value.starts(with: other.value) 19 | } 20 | 21 | func appendPath(other: NormalisedURLPath) throws -> NormalisedURLPath { 22 | return try NormalisedURLPath(input: self.value + other.value) 23 | } 24 | 25 | func getAsStringDangerous() -> String { 26 | return self.value 27 | } 28 | 29 | static func normaliseURLPathOrThrowError(input: String) throws -> String { 30 | var trimmedInput: String = input.trim() 31 | 32 | do { 33 | if !trimmedInput.starts(with: "http://") && !trimmedInput.starts(with: "https://") { 34 | throw SDKFailableError.failableError 35 | } 36 | 37 | guard let url: URL = URL(string: trimmedInput) else { 38 | throw SDKFailableError.failableError 39 | } 40 | 41 | trimmedInput = url.path 42 | 43 | if trimmedInput.hasSuffix("/") { 44 | return trimmedInput.substring(fromIndex: 1) 45 | } 46 | 47 | return trimmedInput 48 | } catch {} 49 | 50 | // not a valid URL 51 | // If the input contains a . it means they have given a domain name. 52 | // So we try assuming that they have given a domain name + path 53 | if (isDomainGiven(input: trimmedInput) || trimmedInput.starts(with: "localhost")) && !trimmedInput.starts(with: "http://") && !trimmedInput.starts(with: "https://") { 54 | trimmedInput = "http://" + trimmedInput 55 | return try NormalisedURLPath.normaliseURLPathOrThrowError(input: trimmedInput) 56 | } 57 | 58 | if trimmedInput.indexOf(character: "/") != trimmedInput.startIndex { 59 | trimmedInput = "/" + trimmedInput 60 | } 61 | 62 | do { 63 | guard let _: URL = URL(string: "http://example.com" + trimmedInput) else { 64 | throw SDKFailableError.failableError 65 | } 66 | 67 | return try normaliseURLPathOrThrowError(input: "http://example.com" + trimmedInput) 68 | } catch { 69 | throw SuperTokensError.initError(message: "Please provide a valid URL path") 70 | } 71 | } 72 | 73 | static func isDomainGiven(input: String) -> Bool { 74 | // If no dot, return false. 75 | if input.indexOf(character: ".") == nil || input.starts(with: "/") { 76 | return false 77 | } 78 | 79 | do { 80 | guard let url: URL = URL(string: input), let hostname: String = url.host else { 81 | throw SDKFailableError.failableError 82 | } 83 | 84 | return hostname.indexOf(character: ".") != nil 85 | } catch {} 86 | 87 | do { 88 | guard let url: URL = URL(string: "http://" + input), let hostname: String = url.host else { 89 | throw SDKFailableError.failableError 90 | } 91 | 92 | return hostname.indexOf(character: ".") != nil 93 | } catch {} 94 | 95 | return false 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | We're so excited you're interested in helping with SuperTokens! We are happy to help you get started, even if you don't have any previous open-source experience :blush: 4 | 5 | ## New to Open Source? 6 | 1. Take a look at [How to Contribute to an Open Source Project on GitHub](https://egghead.io/courses/how-to-contribute-to-an-open-source-project-on-github) 7 | 2. Go thorugh the [SuperTokens Code of Conduct](https://github.com/supertokens/supertokens-ios/blob/master/CODE_OF_CONDUCT.md) 8 | 9 | ## Where to ask Questions? 10 | 1. Check our [Github Issues](https://github.com/supertokens/supertokens-ios/issues) to see if someone has already answered your question. 11 | 2. Join our community on [Discord](https://supertokens.io/discord) and feel free to ask us your questions 12 | 13 | 14 | ## Development Setup 15 | 16 | ### Prerequisites 17 | - OS: macOS 18 | - IDE: XCode 19 | - NodeJS 20 | - Java 21 | 22 | ### Project Setup 23 | 1. Please setup `supertokens-core` by following [this guide](https://github.com/supertokens/supertokens-core/blob/master/CONTRIBUTING.md#development-setup). If you are not contributing to `supertokens-core`, please skip steps 1 & 4 under "Project Setup" section. 24 | 2. Clone the forked repository in the parent directory of the previously setup `supertokens-root`. That is, `supertokens-ios` and `supertokens-root` should exist side by side within the same parent directory. 25 | 3. `cd supertokens-ios` 26 | 4. Add git pre-commit hooks 27 | ``` 28 | ./setup-pre-commit.sh 29 | ``` 30 | 31 | ## Modifying Code 32 | 1. Open the `supertokens-ios` project in XCode by double clicking on `./Example/SuperTokensSession.xcworkspace`. 33 | 2. You can start modifying the code. 34 | 35 | ## Testing 36 | 1. Navigate to the `supertokens-root` repository 37 | 2. Start the testing environment 38 | ``` 39 | ./startTestingEnv --wait 40 | ``` 41 | 3. In a new terminal, navigate to the `supertokens-ios` repository. 42 | 4. Start a node server required for testing 43 | ``` 44 | cd ./testHelpers/server/ 45 | npm i -d 46 | npm i git+https://github.com:supertokens/supertokens-node.git 47 | cd ../.. 48 | ./testHelpers/startServer ../supertokens-root 49 | ``` 50 | 5. Open a new terminal in `supertokens-ios` and run all tests 51 | ``` 52 | xcodebuild test -enableCodeCoverage YES -workspace testHelpers/testapp/SuperTokensSession.xcworkspace -scheme SuperTokensSession-Example -sdk iphonesimulator -destination 'platform=iOS Simulator,OS=16.1,name=iPhone 14 Pro' ONLY_ACTIVE_ARCH=NO 53 | ``` 54 | Alternatively, you can also run all tests via XCode. The tests are present in the `testHelpers/testapp` project. 55 | 6. If all tests pass the output should be: 56 | 57 | IOS tests passing 58 | 59 | 60 | ## Pull Request 61 | 1. Before submitting a pull request make sure all tests have passed 62 | 2. Reference the relevant issue or pull request and give a clear description of changes/features added when submitting a pull request 63 | 64 | ## SuperTokens Community 65 | SuperTokens is made possible by a passionate team and a strong community of developers. If you have any questions or would like to get more involved in the SuperTokens community you can check out: 66 | - [Github Issues](https://github.com/supertokens/supertokens-ios/issues) 67 | - [Discord](https://supertokens.io/discord) 68 | - [Twitter](https://twitter.com/supertokensio) 69 | - or [email us](mailto:team@supertokens.io) 70 | 71 | Additional resources you might find useful: 72 | - [SuperTokens Docs](https://supertokens.io/docs/community/getting-started/installation) 73 | - [Blog Posts](https://supertokens.io/blog/) 74 | -------------------------------------------------------------------------------- /examples/with-thirdparty/README.md: -------------------------------------------------------------------------------- 1 | # SuperTokens Example App 2 | 3 | ## Add dependencies 4 | 5 | This example uses requires the following dependencies: 6 | 7 | - [AppAuth](https://github.com/openid/AppAuth-iOS) 8 | - [GoogleSignIn](https://developers.google.com/identity/sign-in/ios/start-integrating) 9 | - [SuperTokensIOS](https://github.com/supertokens/supertokens-ios) 10 | 11 | This example app uses Swift Package Manager but you can use Cocoapods instead. 12 | 13 | ## Setup 14 | 15 | ### Google 16 | 17 | - Create OAuth credentials for iOS on [Google cloud console](https://console.cloud.google.com/) 18 | - Create OAuth credentials for Web on [Google cloud console](https://console.cloud.google.com/). This is required because we need to get the authorization code in the app to be able to use SuperTokens. You need to provide all values (including domains and URLs) for Google login to work, you can use dummy values if you do not have a web application. 19 | - Replace all occurences of `GOOGLE_IOS_CLIENT_ID` with the client id for iOS in the app's code (including the info.plist) 20 | - Replace `GOOGLE_IOS_URL_SCHEME` with the value of `GOOGLE_IOS_CLIENT_ID` in reverse, for example if the iOS client id is `com.org.scheme` the value you want to set is `scheme.org.com`. Google cloud console will provide a way to copy the URL scheme to make this easier. 21 | - Replace all occurences of `GOOGLE_WEB_CLIENT_ID` with the client id for Web in both the iOS code (including the info.plist) and the backend code 22 | - Replace all occurences of `GOOGLE_WEB_CLIENT_SECRET` with the client secret in the backend code 23 | 24 | ### Github login 25 | 26 | - Create credentials for an OAuth app from Github Developer Settings 27 | - Use com.supertokens.supertokensexample://oauthredirect when configuring the Authorization callback URL. If you are using your own redirect url be sure to update the `onGithubClicked` function in `LoginScreenViewController.swift` 28 | - Replace all occurences of `GITHUB_CLIENT_ID` in both the frontend and backend 29 | - Replace all occurences of `GITHUB_CLIENT_SECRET` in the backend code 30 | 31 | GitHub requires that we pass an additional `Accept: application/json` header when calling the token endpoint but the AppAuth library does not allow us to pass custom headers. In this example app we get around this by registering a custom `URLProtocol` that adds this header for all requests made to the token endpoint. To see how this is done refer to `GithubLoginProtocol.swift`. 32 | 33 | ### Apple login 34 | 35 | - Add the Sign in with Apple capability for your app's primary target. This is already done for this example app so no steps are needed. 36 | - If you are not using Xcode's automatic signing you will need to manually add the capability against your bundle id in Apple's dashboard. 37 | - Replace all occurrences of `APPLE_CLIENT_ID`. This should match your bundle id 38 | - Replace all occurrences of `APPLE_KEY_ID`. You will need to create a new key with the Sign in with Apple capability on Apple's dashboard. 39 | - Replace all occurences of `APPLE_PRIVATE_KEY`, when you create a key there will be an option to download the private key. You can only download this once. 40 | - Replace all occurrences of `APPLE_TEAM_ID` with your Apple developer account's team id 41 | 42 | ## Running the app 43 | 44 | - Replace the value of the API domain in `Constants.swift` and `/backend/config.ts` to match your machines local IP address 45 | - Navigate to the `/backend` folder and run `npm run start` 46 | - Open the app in Xcode and run it on an emulator or simulator 47 | 48 | ## How it works 49 | 50 | - On app launch we check if a session exists and redirect to login if it doesnt 51 | - We register the `SuperTokensURLProtocol` so that the SuperTokens SDK can manage session tokens for us 52 | - After logging in we call APIs exposed by the SuperTokens backend SDKs to create a session and redirect to the home screen 53 | - On the home screen we call a protected API to fetch session information -------------------------------------------------------------------------------- /testHelpers/testapp/Tests/utilsTests.swift: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2020, VRAI Labs and/or its affiliates. All rights reserved. 2 | * 3 | * This software is licensed under the Apache License, Version 2.0 (the 4 | * "License") as published by the Apache Software Foundation. 5 | * 6 | * You may not use this file except in compliance with the License. You may 7 | * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | * License for the specific language governing permissions and limitations 13 | * under the License. 14 | */ 15 | 16 | import XCTest 17 | @testable import SuperTokensIOS 18 | 19 | class utilsTest: XCTestCase { 20 | let fakeGetApi = "https://www.google.com" 21 | 22 | // MARK: Runs after all tests 23 | override class func tearDown() { 24 | let semaphore = DispatchSemaphore(value: 0) 25 | 26 | TestUtils.afterAllTests { 27 | semaphore.signal() 28 | } 29 | _ = semaphore.wait(timeout: DispatchTime.distantFuture) 30 | 31 | super.tearDown() 32 | } 33 | 34 | // MARK: Runs after each test 35 | override func tearDown() { 36 | URLProtocol.unregisterClass(SuperTokensURLProtocol.self) 37 | super.tearDown() 38 | } 39 | 40 | // MARK: Runs before all tests 41 | override class func setUp() { 42 | super.setUp() 43 | 44 | let semaphore = DispatchSemaphore(value: 0) 45 | 46 | TestUtils.beforeAllTests { 47 | semaphore.signal() 48 | } 49 | 50 | _ = semaphore.wait(timeout: DispatchTime.distantFuture) 51 | } 52 | 53 | // MARK: Runs before each test 54 | override func setUp() { 55 | super.setUp() 56 | let semaphore = DispatchSemaphore(value: 0) 57 | HTTPCookieStorage.shared.removeCookies(since: .distantPast) 58 | 59 | TestUtils.beforeEachTest { 60 | semaphore.signal() 61 | } 62 | 63 | _ = semaphore.wait(timeout: DispatchTime.distantFuture) 64 | URLProtocol.registerClass(SuperTokensURLProtocol.self) 65 | } 66 | 67 | func testThatDictionaryLowerCaseExtensionWorksFine() { 68 | var dict: Dictionary = [ 69 | "key": "value", 70 | "CasedKey": "CasedValue", 71 | "WeiRDcaSedKey": "weiRdcasedValUe" 72 | ] 73 | 74 | dict.lowerCaseKeys() 75 | 76 | XCTAssert(dict["key"] == "value") 77 | XCTAssert(dict["casedkey"] == "CasedValue") 78 | XCTAssert(dict["weirdcasedkey"] == "weiRdcasedValUe") 79 | 80 | func dictContainsKey(_ key: String) -> Bool { 81 | return dict.contains(where: { 82 | _key, _ in 83 | 84 | return _key == key 85 | }) 86 | } 87 | 88 | XCTAssert(dictContainsKey("key")) 89 | XCTAssert(!dictContainsKey("CasedKey")) 90 | XCTAssert(!dictContainsKey("WeiRDcaSedKey")) 91 | } 92 | 93 | func testThatSavingHeadersFromResponseIsCaseInsensitive() { 94 | var httpResonse = HTTPURLResponse(url: URL(string: fakeGetApi)!, statusCode: 200, httpVersion: nil, headerFields: [ 95 | "St-Access-Token": "access-token", 96 | "ST-refresh-TOKEN": "refresh-token", 97 | ]) 98 | 99 | Utils.saveTokenFromHeaders(httpResponse: httpResonse!) 100 | 101 | let accessToken = Utils.getTokenForHeaderAuth(tokenType: .access) 102 | let refreshToken = Utils.getTokenForHeaderAuth(tokenType: .refresh) 103 | 104 | XCTAssert(accessToken == "access-token") 105 | XCTAssert(refreshToken == "refresh-token") 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /testHelpers/testapp/SuperTokensSession/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 25 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## [Unreleased] 8 | 9 | ## [0.4.3] - 2025-03-26 10 | 11 | ### Changes 12 | 13 | - Added new FDI version support: 4.1 14 | 15 | ## [0.4.2] - 2024-10-29 16 | 17 | ### Changes 18 | 19 | - Added new FDI version support: 3.1, 4.0 20 | 21 | 22 | ## [0.4.1] - 2024-07-12 23 | 24 | ### Changes 25 | 26 | - Removed redundant calls to `removeToken` 27 | 28 | ## [0.4.0] - 2024-06-05 29 | 30 | ### Changes 31 | 32 | - Fixed the session refresh loop in all the request interceptors that occurred when an API returned a 401 response despite a valid session. Interceptors now attempt to refresh the session a maximum of ten times before throwing an error. The retry limit is configurable via the `maxRetryAttemptsForSessionRefresh` option. 33 | 34 | ## [0.3.2] - 2024-05-28 35 | 36 | - Readds FDI 2.0 and 3.0 support 37 | 38 | ## [0.3.1] - 2024-05-28 39 | 40 | - Adds FDI 2.0 and 3.0 support 41 | 42 | ## [0.3.0] - 2024-05-07 43 | 44 | ### Breaking change 45 | 46 | The `shouldDoInterceptionBasedOnUrl` function now returns true: 47 | - If `sessionTokenBackendDomain` is a valid subdomain of the URL's domain. This aligns with the behavior of browsers when sending cookies to subdomains. 48 | - Even if the ports of the URL you are querying are different compared to the `apiDomain`'s port ot the `sessionTokenBackendDomain` port (as long as the hostname is the same, or a subdomain of the `sessionTokenBackendDomain`): https://github.com/supertokens/supertokens-website/issues/217 49 | 50 | ## [0.2.7] - 2024-03-14 51 | 52 | - New FDI version support: 1.19 53 | - Update test server to work with new node server versions 54 | 55 | ## [0.2.6] - 2023-09-13 56 | 57 | - Adds 1.18 to the list of supported FDI versions 58 | 59 | ## [0.2.5] - 2023-09-13 60 | 61 | - Fixes an issue where session tokens from network responses would not be consumed if they were not in lowercase (Credit: [mattanimation](https://github.com/mattanimation)) 62 | - Adds Swift Package Manager support (Credit: [mattanimation](https://github.com/mattanimation)) 63 | 64 | ## [0.2.4] - 2023-07-31 65 | 66 | - Updates supported FDI versions to include 67 | 68 | ## [0.2.3] - 2023-07-10 69 | 70 | ### Fixes 71 | 72 | - Fixed an issue where the Authorization header was getting removed unnecessarily 73 | 74 | ## [0.2.2] - 2023-06-06 75 | 76 | - Refactors session logic to delete access token and refresh token if the front token is removed. This helps with proxies that strip headers with empty values which would result in the access token and refresh token to persist after signout 77 | 78 | ## [0.2.1] - 2023-05-03 79 | 80 | - Adds tests based on changes in the session management logic in the backend SDKs and SuperTokens core 81 | 82 | ## [0.2.0] - 2023-01-30 83 | 84 | ### Breaking Changes 85 | 86 | - The SDK now only supports FDI version 1.16 87 | - The backend SDK should be updated to a version supporting the header-based sessions! 88 | - supertokens-node: >= 13.0.0 89 | - supertokens-python: >= 0.12.0 90 | - supertokens-golang: >= 0.10.0 91 | - Properties passed when calling SuperTokens.init have been renamed: 92 | - `cookieDomain` -> `sessionTokenBackendDomain` 93 | 94 | ### Added 95 | 96 | - The SDK now supports managing sessions via headers (using `Authorization` bearer tokens) instead of cookies 97 | - A new property has been added when calling SuperTokens.init: `tokenTransferMethod`. This can be used to configure whether the SDK should use cookies or headers for session management (`header` by default). Refer to https://supertokens.com/docs/thirdpartyemailpassword/common-customizations/sessions/token-transfer-method for more information 98 | 99 | ## [0.1.2] - 2022-11-29 100 | 101 | - Fixes an issue with documentation generation 102 | 103 | ## [0.1.1] - 2022-11-29 104 | 105 | - Added documentation generation 106 | 107 | ## [0.1.0] - 2022-10-17 108 | 109 | - Adds support for using SuperTokens across app extensions (using App Groups) 110 | 111 | ## [0.0.1] - 2022-10-12 112 | 113 | - Inial Release 114 | -------------------------------------------------------------------------------- /addReleaseTag: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Expects a releasePassword file to be ./ 3 | 4 | # get version------------ 5 | version=`cat SuperTokensIOS.podspec | grep -e "s.version =" -e "s.version="` 6 | 7 | while IFS='"' read -ra ADDR; do 8 | counter=0 9 | for i in "${ADDR[@]}"; do 10 | if [ $counter == 1 ] 11 | then 12 | version=$i 13 | fi 14 | counter=$(($counter+1)) 15 | done 16 | done <<< "$version" 17 | 18 | branch_name="$(git symbolic-ref HEAD 2>/dev/null)" || 19 | branch_name="(unnamed branch)" # detached HEAD 20 | 21 | branch_name=${branch_name##refs/heads/} 22 | 23 | git fetch --prune --prune-tags 24 | 25 | password=`cat ./apiPassword` 26 | 27 | 28 | # we get from the server is the tests have passed or not. 29 | testPassedJson=`curl -s -X GET \ 30 | "https://api.supertokens.io/0/frontend?password=$password&version=$version&name=ios" \ 31 | -H 'api-version: 0'` 32 | 33 | 34 | if [[ `echo $testPassedJson | jq .testPassed` == "null" ]] 35 | then 36 | testPassed="false" 37 | else 38 | testPassed=`echo $testPassedJson | jq .testPassed` 39 | fi 40 | 41 | if [[ $testPassed != "true" ]] 42 | then 43 | RED='\033[0;31m' 44 | NC='\033[0m' # No Color 45 | printf "${RED}All tests have not passed. So stopping process.${NC}\n" 46 | exit 1 47 | fi 48 | 49 | # check that current commit has a dev tag and that it is the correct version 50 | # get current commit hash------------ 51 | if [ $# -eq 0 ] 52 | then 53 | commit_hash=`git log --pretty=format:'%H' -n 1` 54 | else 55 | commit_hash=$1 56 | fi 57 | 58 | 59 | # check if current commit already has a tag or not------------ 60 | currTag=`git tag -l --points-at $commit_hash` 61 | 62 | expectedCurrTag=dev-v$version 63 | 64 | if [[ $currTag == $expectedCurrTag ]] 65 | then 66 | continue=1 67 | else 68 | RED='\033[0;31m' 69 | NC='\033[0m' 70 | printf "${RED}This commit does not have the right tag for the version you want to release.${NC}\n" 71 | exit 1 72 | fi 73 | 74 | releasePassword=`cat ./releasePassword` 75 | 76 | # now we call the patch API to make it release mode 77 | responseStatus=`curl -s -o /dev/null -w "%{http_code}" -X PATCH \ 78 | https://api.supertokens.io/0/frontend \ 79 | -H 'Content-Type: application/json' \ 80 | -H 'api-version: 0' \ 81 | -d "{ 82 | \"password\": \"$releasePassword\", 83 | \"name\":\"ios\", 84 | \"version\":\"$version\", 85 | \"release\": true 86 | }"` 87 | 88 | if [ $responseStatus -ne "200" ] 89 | then 90 | RED='\033[0;31m' 91 | NC='\033[0m' 92 | printf "${RED}patch api failed. Please try again.${NC}\n" 93 | exit 1 94 | fi 95 | 96 | git tag --delete $currTag 97 | git push --delete origin $currTag 98 | 99 | git tag v$version 100 | git push --tags 101 | 102 | 103 | response=`curl -s -X GET \ 104 | "https://api.supertokens.io/0/frontend/latest/check?password=$password&version=$version&name=ios" \ 105 | -H 'api-version: 0'` 106 | response=`echo $response | jq .isLatest` 107 | 108 | 109 | if [[ $response == "null" ]] 110 | then 111 | RED='\033[0;31m' 112 | NC='\033[0m' 113 | printf "${RED}error while determining if we should push to master or not. Please do so manually if needed:${NC}\n" 114 | if [[ $branch_name == "(unnamed branch)" ]] 115 | then 116 | echo "git checkout -b forrelease" 117 | echo "git merge master" 118 | echo "git checkout master" 119 | echo "git merge forrelease" 120 | echo "git push" 121 | echo "git checkout forrelease" 122 | exit 1 123 | else 124 | echo "git merge master" 125 | echo "git checkout master" 126 | echo "git merge $branch_name" 127 | echo "git push" 128 | echo "git checkout $branch_name" 129 | exit 1 130 | fi 131 | 132 | fi 133 | 134 | if [[ $response == "true" ]] 135 | then 136 | echo "pushing to mater..." 137 | if [[ $branch_name == "(unnamed branch)" ]] 138 | then 139 | git checkout -b forrelease 140 | git merge master 141 | git checkout master 142 | git merge forrelease 143 | git push 144 | git checkout forrelease 145 | echo "Done! Please delete this branch" 146 | else 147 | git merge master 148 | git checkout master 149 | git merge $branch_name 150 | git push 151 | git checkout $branch_name 152 | echo "Done!" 153 | fi 154 | fi -------------------------------------------------------------------------------- /examples/with-thirdparty/with-thirdparty/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /examples/with-thirdparty/with-thirdparty/LoginScreen/LoginView.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 30 | 38 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /testHelpers/testapp/SuperTokensSession.xcodeproj/xcshareddata/xcschemes/SuperTokensSession-Example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 43 | 49 | 50 | 51 | 52 | 53 | 58 | 59 | 65 | 66 | 67 | 68 | 70 | 76 | 77 | 78 | 79 | 80 | 90 | 92 | 98 | 99 | 100 | 101 | 107 | 109 | 115 | 116 | 117 | 118 | 120 | 121 | 124 | 125 | 126 | -------------------------------------------------------------------------------- /SuperTokensIOS/Classes/FrontToken.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FrontToken.swift 3 | // SuperTokensSession 4 | // 5 | // Created by Nemi Shah on 30/09/22. 6 | // 7 | 8 | import Foundation 9 | 10 | internal class FrontToken { 11 | static var tokenInMemory: String? = nil 12 | static var userDefaultsKey: String = "supertokens-ios-fronttoken-key" 13 | private static let readWriteDispatchQueue = DispatchQueue(label: "io.supertokens.fronttoken.concurrent", attributes: .concurrent) 14 | private static var tokenInfoSemaphore = DispatchSemaphore(value: 0) 15 | 16 | private static func getFrontTokenFromStorage() -> String? { 17 | if tokenInMemory == nil { 18 | tokenInMemory = Utils.getUserDefaults().string(forKey: userDefaultsKey) 19 | } 20 | 21 | return tokenInMemory 22 | } 23 | 24 | private static func getFrontToken() -> String? { 25 | if Utils.getLocalSessionState().status == .NOT_EXISTS { 26 | return nil 27 | } 28 | 29 | return getFrontTokenFromStorage() 30 | } 31 | 32 | private static func parseFrontToken(frontTokenDecoded: String) -> [String: Any] { 33 | // In the event that the access token is not a valid base64 encoded json string, this will throw a runtime error 34 | let base64decodedData: Data = Data(base64Encoded: frontTokenDecoded)! 35 | let decodedString: String = String(data: base64decodedData, encoding: .utf8)! 36 | 37 | return try! JSONSerialization.jsonObject(with: decodedString.data(using: .utf8)!) as! [String: Any] 38 | } 39 | 40 | private static func getTokenInfo() -> [String: Any]? { 41 | var finalReturnValue: [String: Any]? = nil 42 | let executionSemaphore = DispatchSemaphore(value: 0) 43 | 44 | readWriteDispatchQueue.async { 45 | while (true) { 46 | let frontToken: String? = getFrontToken() 47 | 48 | if frontToken == nil { 49 | let localSessionState = Utils.getLocalSessionState() 50 | if localSessionState.status == .EXISTS { 51 | tokenInfoSemaphore.wait() 52 | } else { 53 | finalReturnValue = nil 54 | executionSemaphore.signal() 55 | break 56 | } 57 | } else { 58 | finalReturnValue = parseFrontToken(frontTokenDecoded: frontToken!) 59 | executionSemaphore.signal() 60 | break 61 | } 62 | } 63 | } 64 | 65 | executionSemaphore.wait() 66 | return finalReturnValue 67 | } 68 | 69 | static func getToken() -> [String: Any]? { 70 | return getTokenInfo() 71 | } 72 | 73 | private static func setFrontTokenToStorage(frontToken: String?) { 74 | Utils.getUserDefaults().set(frontToken, forKey: userDefaultsKey) 75 | tokenInMemory = frontToken 76 | } 77 | 78 | private static func setFrontToken(frontToken: String?) { 79 | let oldToken = getFrontTokenFromStorage() 80 | 81 | if oldToken != nil && frontToken != nil { 82 | let oldTokenPayload: [String: Any] = parseFrontToken(frontTokenDecoded: oldToken!)["up"] as! [String : Any] 83 | let newPayload: [String: Any] = parseFrontToken(frontTokenDecoded: frontToken!)["up"] as! [String : Any] 84 | 85 | let oldPayloadString = String(data: try! JSONSerialization.data(withJSONObject: oldTokenPayload), encoding: .utf8)! 86 | let newPayloadString = String(data: try! JSONSerialization.data(withJSONObject: newPayload), encoding: .utf8)! 87 | 88 | if oldPayloadString != newPayloadString { 89 | SuperTokens.config!.eventHandler(.ACCESS_TOKEN_PAYLOAD_UPDATED) 90 | } 91 | } 92 | 93 | setFrontTokenToStorage(frontToken: frontToken) 94 | } 95 | 96 | private static func removeTokenFromStorage() { 97 | Utils.getUserDefaults().removeObject(forKey: userDefaultsKey) 98 | tokenInMemory = nil 99 | } 100 | 101 | static func removeToken() { 102 | AntiCSRF.removeToken() 103 | let executionSemaphore = DispatchSemaphore(value: 0) 104 | 105 | readWriteDispatchQueue.async(flags: .barrier) { 106 | removeTokenFromStorage() 107 | Utils.setToken(tokenType: .access, value: "") 108 | Utils.setToken(tokenType: .refresh, value: "") 109 | tokenInfoSemaphore.signal() 110 | executionSemaphore.signal() 111 | } 112 | 113 | executionSemaphore.wait() 114 | } 115 | 116 | static func setItem(frontToken: String) { 117 | // We update the refresh attempt info here as well, since this means that we've updated the session in some way 118 | // This could be both by a refresh call or if the access token was updated in a custom endpoint 119 | // By saving every time the access token has been updated, we cause an early retry if 120 | // another request has failed with a 401 with the previous access token and the token still exists. 121 | // Check the start and end of onUnauthorisedResponse 122 | // As a side-effect we reload the anti-csrf token to check if it was changed by another tab. 123 | Utils.saveLastAccessTokenUpdate() 124 | 125 | if frontToken == "remove" { 126 | FrontToken.removeToken() 127 | return 128 | } 129 | 130 | FrontToken.setFrontToken(frontToken: frontToken) 131 | } 132 | 133 | static func doesTokenExist() -> Bool { 134 | let frontToken = FrontToken.getFrontTokenFromStorage() 135 | return frontToken != nil 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /examples/with-thirdparty/backend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Visit https://aka.ms/tsconfig.json to read more about this file */ 4 | /* Basic Options */ 5 | // "incremental": true, /* Enable incremental compilation */ 6 | "target": "es5" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */, 7 | "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */, 8 | // "lib": [], /* Specify library files to be included in the compilation. */ 9 | // "allowJs": true, /* Allow javascript files to be compiled. */ 10 | // "checkJs": true, /* Report errors in .js files. */ 11 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ 12 | // "declaration": true, /* Generates corresponding '.d.ts' file. */ 13 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ 14 | // "sourceMap": true, /* Generates corresponding '.map' file. */ 15 | // "outFile": "./", /* Concatenate and emit output to single file. */ 16 | // "outDir": "./", /* Redirect output structure to the directory. */ 17 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 18 | // "composite": true, /* Enable project compilation */ 19 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ 20 | // "removeComments": true, /* Do not emit comments to output. */ 21 | // "noEmit": true, /* Do not emit outputs. */ 22 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 23 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 24 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 25 | /* Strict Type-Checking Options */ 26 | "strict": true /* Enable all strict type-checking options. */, 27 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 28 | // "strictNullChecks": true, /* Enable strict null checks. */ 29 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */ 30 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ 31 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ 32 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 33 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 34 | /* Additional Checks */ 35 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 36 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 37 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 38 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 39 | /* Module Resolution Options */ 40 | // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 41 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 42 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 43 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 44 | // "typeRoots": [], /* List of folders to include type definitions from. */ 45 | // "types": [], /* Type declaration files to be included in compilation. */ 46 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 47 | "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, 48 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 49 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 50 | /* Source Map Options */ 51 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 52 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 53 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 54 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 55 | /* Experimental Options */ 56 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 57 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 58 | /* Advanced Options */ 59 | "skipLibCheck": true /* Skip type checking of declaration files. */, 60 | "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /testHelpers/server/utils.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2020, VRAI Labs and/or its affiliates. All rights reserved. 2 | * 3 | * This software is licensed under the Apache License, Version 2.0 (the 4 | * "License") as published by the Apache Software Foundation. 5 | * 6 | * You may not use this file except in compliance with the License. You may 7 | * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | * License for the specific language governing permissions and limitations 13 | * under the License. 14 | */ 15 | const { exec } = require("child_process"); 16 | let fs = require("fs"); 17 | 18 | module.exports.executeCommand = async function(cmd) { 19 | return new Promise((resolve, reject) => { 20 | console.log("Executing command: " + cmd); 21 | exec(cmd, (err, stdout, stderr) => { 22 | if (err) { 23 | reject({err, stdout, stderr}); 24 | return; 25 | } 26 | resolve({ stdout, stderr }); 27 | }); 28 | }); 29 | }; 30 | 31 | module.exports.setupST = async function() { 32 | let installationPath = process.env.INSTALL_PATH; 33 | try { 34 | await module.exports.executeCommand("cd " + installationPath + " && cp temp/licenseKey ./licenseKey"); 35 | } catch (ignored) {} 36 | await module.exports.executeCommand("cd " + installationPath + " && cp temp/config.yaml ./config.yaml"); 37 | }; 38 | 39 | module.exports.setKeyValueInConfig = async function(key, value) { 40 | return new Promise((resolve, reject) => { 41 | let installationPath = process.env.INSTALL_PATH; 42 | fs.readFile(installationPath + "/config.yaml", "utf8", function(err, data) { 43 | if (err) { 44 | reject(err); 45 | return; 46 | } 47 | let oldStr = new RegExp("((#\\s)?)" + key + "(:|((:\\s).+))\n"); 48 | let newStr = key + ": " + value + "\n"; 49 | let result = data.replace(oldStr, newStr); 50 | fs.writeFile(installationPath + "/config.yaml", result, "utf8", function(err) { 51 | if (err) { 52 | reject(err); 53 | } else { 54 | resolve(); 55 | } 56 | }); 57 | }); 58 | }); 59 | }; 60 | 61 | module.exports.cleanST = async function() { 62 | let installationPath = process.env.INSTALL_PATH; 63 | try { 64 | await module.exports.executeCommand("cd " + installationPath + " && rm licenseKey"); 65 | } catch (ignored) {} 66 | await module.exports.executeCommand("cd " + installationPath + " && rm config.yaml"); 67 | await module.exports.executeCommand("cd " + installationPath + " && rm -rf .webserver-temp-*"); 68 | await module.exports.executeCommand("cd " + installationPath + " && rm -rf .started"); 69 | }; 70 | 71 | module.exports.stopST = async function(pid) { 72 | let pidsBefore = await getListOfPids(); 73 | if (pidsBefore.length === 0) { 74 | return; 75 | } 76 | await module.exports.executeCommand("kill " + pid); 77 | let startTime = Date.now(); 78 | while (Date.now() - startTime < 10000) { 79 | let pidsAfter = await getListOfPids(); 80 | if (pidsAfter.includes(pid)) { 81 | await new Promise(r => setTimeout(r, 100)); 82 | continue; 83 | } else { 84 | return; 85 | } 86 | } 87 | throw new Error("error while stopping ST with PID: " + pid); 88 | }; 89 | 90 | module.exports.killAllST = async function() { 91 | let pids = await getListOfPids(); 92 | for (let i = 0; i < pids.length; i++) { 93 | await module.exports.stopST(pids[i]); 94 | } 95 | }; 96 | 97 | module.exports.startST = async function(host = "localhost", port = 9000) { 98 | return new Promise(async (resolve, reject) => { 99 | let installationPath = process.env.INSTALL_PATH; 100 | let returned = false; 101 | module.exports 102 | .executeCommand( 103 | "cd " + 104 | installationPath + 105 | ` && java -Djava.security.egd=file:/dev/urandom -classpath "./core/*:./plugin-interface/*" io.supertokens.Main ./ DEV host=` + 106 | host + 107 | " port=" + 108 | port + 109 | " test_mode" 110 | ) 111 | .catch(({err, stdout, stderr}) => { 112 | if (!returned) { 113 | console.log("Starting ST failed: java command returned early w/ non-zero exit code"); 114 | console.log(err); 115 | console.log(stdout); 116 | console.log(stderr); 117 | returned = true; 118 | reject(err); 119 | } 120 | }); 121 | let startTime = Date.now(); 122 | let helloResp; 123 | while (Date.now() - startTime < 10000) { 124 | try { 125 | helloResp = await fetch(`http://${host}:${port}/hello`); 126 | if (helloResp.status === 200) { 127 | console.log("Started ST, it's saying: " + await helloResp.text()); 128 | resolve(); 129 | returned = true; 130 | return; 131 | } 132 | } catch (ex) { 133 | console.log("Waiting for ST to start, caught exception: " + ex); 134 | // We expect (and ignore) network errors here 135 | } 136 | await new Promise(r => setTimeout(r, 100)); 137 | } 138 | console.log(helloResp); 139 | reject("Starting ST process timed out"); 140 | }); 141 | }; 142 | 143 | async function getListOfPids() { 144 | let installationPath = process.env.INSTALL_PATH; 145 | try { 146 | (await module.exports.executeCommand("cd " + installationPath + " && ls .started/")).stdout; 147 | } catch (err) { 148 | return []; 149 | } 150 | let currList = (await module.exports.executeCommand("cd " + installationPath + " && ls .started/")).stdout; 151 | currList = currList.split("\n"); 152 | let result = []; 153 | for (let i = 0; i < currList.length; i++) { 154 | let item = currList[i]; 155 | if (item === "") { 156 | continue; 157 | } 158 | try { 159 | let pid = (await module.exports.executeCommand("cd " + installationPath + " && cat .started/" + item)) 160 | .stdout; 161 | result.push(pid); 162 | } catch (err) {} 163 | } 164 | return result; 165 | } 166 | 167 | module.exports.maxVersion = function(version1, version2) { 168 | let splittedv1 = version1.split("."); 169 | let splittedv2 = version2.split("."); 170 | let minLength = Math.min(splittedv1.length, splittedv2.length); 171 | for (let i = 0; i < minLength; i++) { 172 | let v1 = Number(splittedv1[i]); 173 | let v2 = Number(splittedv2[i]); 174 | if (v1 > v2) { 175 | return version1; 176 | } else if (v2 > v1) { 177 | return version2; 178 | } 179 | } 180 | if (splittedv1.length >= splittedv2.length) { 181 | return version1; 182 | } 183 | return version2; 184 | }; 185 | 186 | module.exports.isProtectedPropName = function (name) { 187 | return [ 188 | "sub", 189 | "iat", 190 | "exp", 191 | "sessionHandle", 192 | "parentRefreshTokenHash1", 193 | "refreshTokenHash1", 194 | "antiCsrfToken" 195 | ].includes(name); 196 | }; 197 | -------------------------------------------------------------------------------- /testHelpers/testapp/Tests/accessTokenTests.swift: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2020, VRAI Labs and/or its affiliates. All rights reserved. 2 | * 3 | * This software is licensed under the Apache License, Version 2.0 (the 4 | * "License") as published by the Apache Software Foundation. 5 | * 6 | * You may not use this file except in compliance with the License. You may 7 | * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | * License for the specific language governing permissions and limitations 13 | * under the License. 14 | */ 15 | 16 | import XCTest 17 | @testable import SuperTokensIOS 18 | 19 | class accessTokenTests: XCTestCase { 20 | let fakeGetApi = "https://www.google.com" 21 | let refreshCustomHeader = "\(testAPIBase)/refreshHeader" 22 | 23 | // MARK: Runs after all tests 24 | override class func tearDown() { 25 | let semaphore = DispatchSemaphore(value: 0) 26 | 27 | TestUtils.afterAllTests { 28 | semaphore.signal() 29 | } 30 | _ = semaphore.wait(timeout: DispatchTime.distantFuture) 31 | 32 | super.tearDown() 33 | } 34 | 35 | // MARK: Runs after each test 36 | override func tearDown() { 37 | URLProtocol.unregisterClass(SuperTokensURLProtocol.self) 38 | super.tearDown() 39 | } 40 | 41 | // MARK: Runs before all tests 42 | override class func setUp() { 43 | super.setUp() 44 | 45 | let semaphore = DispatchSemaphore(value: 0) 46 | 47 | TestUtils.beforeAllTests { 48 | semaphore.signal() 49 | } 50 | 51 | _ = semaphore.wait(timeout: DispatchTime.distantFuture) 52 | } 53 | 54 | // MARK: Runs before each test 55 | override func setUp() { 56 | super.setUp() 57 | let semaphore = DispatchSemaphore(value: 0) 58 | HTTPCookieStorage.shared.removeCookies(since: .distantPast) 59 | 60 | TestUtils.beforeEachTest { 61 | semaphore.signal() 62 | } 63 | 64 | _ = semaphore.wait(timeout: DispatchTime.distantFuture) 65 | URLProtocol.registerClass(SuperTokensURLProtocol.self) 66 | } 67 | 68 | // MARK: Tests 69 | func testThatAppropriateAccessTokenPayloadIsReturned() { 70 | TestUtils.startST(validity: 3) 71 | 72 | do { 73 | try SuperTokens.initialize(apiDomain: testAPIBase) 74 | } catch { 75 | XCTFail() 76 | } 77 | 78 | do { 79 | let requestSemaphore = DispatchSemaphore(value: 0) 80 | 81 | URLSession.shared.dataTask(with: TestUtils.getLoginRequest(), completionHandler: { 82 | data, response, error in 83 | 84 | if error != nil { 85 | XCTFail("Login request failed") 86 | requestSemaphore.signal() 87 | return 88 | } 89 | 90 | if response as? HTTPURLResponse != nil { 91 | let httpResponse = response as! HTTPURLResponse 92 | if httpResponse.statusCode != 200 { 93 | XCTFail() 94 | } 95 | 96 | requestSemaphore.signal() 97 | } else { 98 | XCTFail("Login API responded with non 200 status") 99 | requestSemaphore.signal() 100 | } 101 | }).resume() 102 | 103 | _ = requestSemaphore.wait(timeout: DispatchTime.distantFuture) 104 | 105 | var payload = try! SuperTokens.getAccessTokenPayloadSecurely() 106 | 107 | if try! TestUtils.checkIfV3AccessTokenIsSupported() { 108 | var expectedKeys = [ 109 | "sub", 110 | "exp", 111 | "iat", 112 | "sessionHandle", 113 | "refreshTokenHash1", 114 | "parentRefreshTokenHash1", 115 | "antiCsrfToken", 116 | "iss" 117 | ] 118 | 119 | if payload.contains(where: { 120 | key, _ in 121 | 122 | return key == "tId" 123 | }) { 124 | expectedKeys.append("tId") 125 | } 126 | 127 | if payload.contains(where: { 128 | key, _ in 129 | 130 | return key == "rsub" 131 | }) { 132 | expectedKeys.append("rsub") 133 | } 134 | 135 | XCTAssert(payload.count == expectedKeys.count) 136 | for (key, _) in payload { 137 | XCTAssert(expectedKeys.contains(key)) 138 | } 139 | } else { 140 | XCTAssert(payload.count == 0) 141 | } 142 | } 143 | } 144 | 145 | func testThatASessionCreatedWithCDI_2_18_CanBeRefreshed() { 146 | TestUtils.startST(validity: 3) 147 | 148 | do { 149 | try SuperTokens.initialize(apiDomain: testAPIBase) 150 | } catch { 151 | XCTFail() 152 | } 153 | 154 | do { 155 | let requestSemaphore = DispatchSemaphore(value: 0) 156 | 157 | URLSession.shared.dataTask(with: TestUtils.getLoginRequest_2_18(), completionHandler: { 158 | data, response, error in 159 | 160 | if error != nil { 161 | XCTFail("Login request failed") 162 | requestSemaphore.signal() 163 | return 164 | } 165 | 166 | if response as? HTTPURLResponse != nil { 167 | let httpResponse = response as! HTTPURLResponse 168 | if httpResponse.statusCode != 200 { 169 | XCTFail() 170 | } 171 | 172 | requestSemaphore.signal() 173 | } else { 174 | XCTFail("Login API responded with non 200 status") 175 | requestSemaphore.signal() 176 | } 177 | }).resume() 178 | 179 | _ = requestSemaphore.wait(timeout: DispatchTime.distantFuture) 180 | 181 | var payload218 = try! SuperTokens.getAccessTokenPayloadSecurely() 182 | 183 | XCTAssert(payload218.count == 1) 184 | XCTAssert((payload218["asdf"] as! Int) == 1) 185 | 186 | _ = try! SuperTokens.attemptRefreshingSession() 187 | 188 | if try! TestUtils.checkIfV3AccessTokenIsSupported() { 189 | let v3Payload = try! SuperTokens.getAccessTokenPayloadSecurely() 190 | 191 | var expectedKeys = [ 192 | "sub", 193 | "exp", 194 | "iat", 195 | "sessionHandle", 196 | "refreshTokenHash1", 197 | "parentRefreshTokenHash1", 198 | "antiCsrfToken", 199 | "asdf" 200 | ] 201 | 202 | if v3Payload.contains(where: { 203 | key, _ in 204 | 205 | return key == "tId" 206 | }) { 207 | expectedKeys.append("tId") 208 | } 209 | 210 | if v3Payload.contains(where: { 211 | key, _ in 212 | 213 | return key == "rsub" 214 | }) { 215 | expectedKeys.append("rsub") 216 | } 217 | 218 | XCTAssert(v3Payload.count == expectedKeys.count) 219 | for (key, _) in v3Payload { 220 | XCTAssert(expectedKeys.contains(key)) 221 | } 222 | } else { 223 | let v2Payload = try! SuperTokens.getAccessTokenPayloadSecurely() 224 | 225 | XCTAssert(v2Payload.count == 1) 226 | XCTAssert((v2Payload["asdf"] as! Int) == 1) 227 | } 228 | } 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /examples/with-thirdparty/with-thirdparty/LoginScreen/LoginScreenViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LoginScreenViewController.swift 3 | // with-thirdparty 4 | // 5 | // Created by Nemi Shah on 10/11/23. 6 | // 7 | 8 | import UIKit 9 | import GoogleSignIn 10 | import AppAuth 11 | import AuthenticationServices 12 | 13 | class LoginScreenViewController: UIViewController, ASAuthorizationControllerDelegate, ASAuthorizationControllerPresentationContextProviding { 14 | 15 | override func viewDidLoad() { 16 | super.viewDidLoad() 17 | } 18 | 19 | @IBAction func onGoogleCliked() { 20 | GIDSignIn.sharedInstance.signIn(withPresenting: self) { signInResult, error in 21 | guard error == nil else { return } 22 | 23 | guard let authCode: String = signInResult?.serverAuthCode as? String else { 24 | print("Google login did not return an authorisation code") 25 | return 26 | } 27 | 28 | let url = URL(string: Constants.apiDomain + "/auth/signinup") 29 | var request = URLRequest(url: url!) 30 | request.httpMethod = "POST" 31 | 32 | let data = try! JSONSerialization.data(withJSONObject: [ 33 | "thirdPartyId": "google", 34 | "redirectURIInfo": [ 35 | // For native flows we do not have a redirect uri 36 | "redirectURIOnProviderDashboard": "", 37 | "redirectURIQueryParams": [ 38 | "code": authCode 39 | ], 40 | ], 41 | ]) 42 | request.httpBody = data 43 | request.setValue("Application/json", forHTTPHeaderField: "Content-Type") 44 | 45 | URLSession.shared.dataTask(with: request) { 46 | data, response, error in 47 | 48 | if error != nil { 49 | print("Google login failed: \(error!.localizedDescription)") 50 | } 51 | 52 | if let _response: URLResponse = response, let httpResponse: HTTPURLResponse = _response as? HTTPURLResponse { 53 | if httpResponse.statusCode == 200 { 54 | DispatchQueue.main.async { [weak self] in 55 | self?.navigationController?.pushViewController(HomeScreenViewController(nibName: "HomeView", bundle: nil), animated: true) 56 | } 57 | } else { 58 | print("SuperTokens API failed with code: \(httpResponse.statusCode)") 59 | } 60 | } 61 | }.resume() 62 | } 63 | } 64 | 65 | @IBAction func onGithubClicked() { 66 | let authorizationEndpoint = URL(string: "https://github.com/login/oauth/authorize")! 67 | let tokenEndpoint = URL(string: "https://github.com/login/oauth/access_token")! 68 | let configuration = OIDServiceConfiguration(authorizationEndpoint: authorizationEndpoint, tokenEndpoint: tokenEndpoint) 69 | 70 | let request = OIDAuthorizationRequest.init(configuration: configuration, 71 | clientId: "GITHUB_CLIENT_ID", 72 | scopes: ["user"], 73 | redirectURL: URL(string: "com.supertokens.supertokensexample://oauthredirect")!, 74 | responseType: OIDResponseTypeCode, 75 | additionalParameters: nil) 76 | 77 | // performs authentication request 78 | print("Initiating authorization request with scope: \(request.scope ?? "nil")") 79 | 80 | let appDelegate = UIApplication.shared.delegate as! AppDelegate 81 | 82 | appDelegate.currentAuthorizationFlow = OIDAuthorizationService.present(request, presenting: self, callback: { 83 | response, error in 84 | 85 | if let response = response, let authCode: String = response.authorizationCode { 86 | let url = URL(string: Constants.apiDomain + "/auth/signinup") 87 | var request = URLRequest(url: url!) 88 | request.httpMethod = "POST" 89 | 90 | let data = try! JSONSerialization.data(withJSONObject: [ 91 | "thirdPartyId": "github", 92 | "redirectURIInfo": [ 93 | // For native flows we do not have a redirect uri 94 | "redirectURIOnProviderDashboard": "com.supertokens.supertokensexample://oauthredirect", 95 | "redirectURIQueryParams": [ 96 | "code": authCode 97 | ], 98 | ], 99 | ]) 100 | request.httpBody = data 101 | request.setValue("Application/json", forHTTPHeaderField: "Content-Type") 102 | 103 | URLSession.shared.dataTask(with: request) { 104 | data, response, error in 105 | 106 | if error != nil { 107 | print("Github login failed: \(error!.localizedDescription)") 108 | } 109 | 110 | if let _response: URLResponse = response, let httpResponse: HTTPURLResponse = _response as? HTTPURLResponse { 111 | if httpResponse.statusCode == 200 { 112 | DispatchQueue.main.async { [weak self] in 113 | self?.navigationController?.pushViewController(HomeScreenViewController(nibName: "HomeView", bundle: nil), animated: true) 114 | } 115 | } else { 116 | print("SuperTokens API failed with code: \(httpResponse.statusCode)") 117 | } 118 | } 119 | }.resume() 120 | } else { 121 | print("Github login failed") 122 | } 123 | }) 124 | } 125 | 126 | func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor { 127 | return self.view.window! 128 | 129 | } 130 | 131 | @IBAction func onAppleClicked() { 132 | let appleIDProvider = ASAuthorizationAppleIDProvider() 133 | let request = appleIDProvider.createRequest() 134 | request.requestedScopes = [.email] 135 | 136 | let authorizationController = ASAuthorizationController(authorizationRequests: [request]) 137 | authorizationController.delegate = self 138 | authorizationController.presentationContextProvider = self 139 | authorizationController.performRequests() 140 | } 141 | 142 | func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) { 143 | guard case let appleIDCredential as ASAuthorizationAppleIDCredential = authorization.credential, let authCodeResponse: Data = appleIDCredential.authorizationCode else { 144 | print("Apple returned an unexpected response") 145 | return 146 | } 147 | 148 | let authCode = String(decoding: authCodeResponse, as: UTF8.self) 149 | 150 | let url = URL(string: Constants.apiDomain + "/auth/signinup") 151 | var request = URLRequest(url: url!) 152 | request.httpMethod = "POST" 153 | 154 | let data = try! JSONSerialization.data(withJSONObject: [ 155 | "thirdPartyId": "apple", 156 | "redirectURIInfo": [ 157 | // For native flows we do not have a redirect uri 158 | "redirectURIOnProviderDashboard": "", 159 | "redirectURIQueryParams": [ 160 | "code": authCode 161 | ], 162 | ], 163 | ]) 164 | request.httpBody = data 165 | request.setValue("Application/json", forHTTPHeaderField: "Content-Type") 166 | 167 | URLSession.shared.dataTask(with: request) { 168 | data, response, error in 169 | 170 | if error != nil { 171 | print("Apple login failed: \(error!.localizedDescription)") 172 | } 173 | 174 | if let _response: URLResponse = response, let httpResponse: HTTPURLResponse = _response as? HTTPURLResponse { 175 | if httpResponse.statusCode == 200 { 176 | DispatchQueue.main.async { [weak self] in 177 | self?.navigationController?.pushViewController(HomeScreenViewController(nibName: "HomeView", bundle: nil), animated: true) 178 | } 179 | } else { 180 | print("SuperTokens API failed with code: \(httpResponse.statusCode)") 181 | } 182 | } 183 | }.resume() 184 | } 185 | 186 | func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) { 187 | print("Apple login failed: \(error.localizedDescription)") 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /testHelpers/testapp/Pods/Target Support Files/Pods-SuperTokensSession_Example/Pods-SuperTokensSession_Example-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | set -u 4 | set -o pipefail 5 | 6 | function on_error { 7 | echo "$(realpath -mq "${0}"):$1: error: Unexpected failure" 8 | } 9 | trap 'on_error $LINENO' ERR 10 | 11 | if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then 12 | # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy 13 | # frameworks to, so exit 0 (signalling the script phase was successful). 14 | exit 0 15 | fi 16 | 17 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 18 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 19 | 20 | COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}" 21 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 22 | BCSYMBOLMAP_DIR="BCSymbolMaps" 23 | 24 | 25 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 26 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 27 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 28 | 29 | # Copies and strips a vendored framework 30 | install_framework() 31 | { 32 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 33 | local source="${BUILT_PRODUCTS_DIR}/$1" 34 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 35 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 36 | elif [ -r "$1" ]; then 37 | local source="$1" 38 | fi 39 | 40 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 41 | 42 | if [ -L "${source}" ]; then 43 | echo "Symlinked..." 44 | source="$(readlink "${source}")" 45 | fi 46 | 47 | if [ -d "${source}/${BCSYMBOLMAP_DIR}" ]; then 48 | # Locate and install any .bcsymbolmaps if present, and remove them from the .framework before the framework is copied 49 | find "${source}/${BCSYMBOLMAP_DIR}" -name "*.bcsymbolmap"|while read f; do 50 | echo "Installing $f" 51 | install_bcsymbolmap "$f" "$destination" 52 | rm "$f" 53 | done 54 | rmdir "${source}/${BCSYMBOLMAP_DIR}" 55 | fi 56 | 57 | # Use filter instead of exclude so missing patterns don't throw errors. 58 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 59 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 60 | 61 | local basename 62 | basename="$(basename -s .framework "$1")" 63 | binary="${destination}/${basename}.framework/${basename}" 64 | 65 | if ! [ -r "$binary" ]; then 66 | binary="${destination}/${basename}" 67 | elif [ -L "${binary}" ]; then 68 | echo "Destination binary is symlinked..." 69 | dirname="$(dirname "${binary}")" 70 | binary="${dirname}/$(readlink "${binary}")" 71 | fi 72 | 73 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 74 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 75 | strip_invalid_archs "$binary" 76 | fi 77 | 78 | # Resign the code if required by the build settings to avoid unstable apps 79 | code_sign_if_enabled "${destination}/$(basename "$1")" 80 | 81 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 82 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 83 | local swift_runtime_libs 84 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u) 85 | for lib in $swift_runtime_libs; do 86 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 87 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 88 | code_sign_if_enabled "${destination}/${lib}" 89 | done 90 | fi 91 | } 92 | # Copies and strips a vendored dSYM 93 | install_dsym() { 94 | local source="$1" 95 | warn_missing_arch=${2:-true} 96 | if [ -r "$source" ]; then 97 | # Copy the dSYM into the targets temp dir. 98 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" 99 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" 100 | 101 | local basename 102 | basename="$(basename -s .dSYM "$source")" 103 | binary_name="$(ls "$source/Contents/Resources/DWARF")" 104 | binary="${DERIVED_FILES_DIR}/${basename}.dSYM/Contents/Resources/DWARF/${binary_name}" 105 | 106 | # Strip invalid architectures from the dSYM. 107 | if [[ "$(file "$binary")" == *"Mach-O "*"dSYM companion"* ]]; then 108 | strip_invalid_archs "$binary" "$warn_missing_arch" 109 | fi 110 | if [[ $STRIP_BINARY_RETVAL == 0 ]]; then 111 | # Move the stripped file into its final destination. 112 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" 113 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.dSYM" "${DWARF_DSYM_FOLDER_PATH}" 114 | else 115 | # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. 116 | mkdir -p "${DWARF_DSYM_FOLDER_PATH}" 117 | touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.dSYM" 118 | fi 119 | fi 120 | } 121 | 122 | # Used as a return value for each invocation of `strip_invalid_archs` function. 123 | STRIP_BINARY_RETVAL=0 124 | 125 | # Strip invalid architectures 126 | strip_invalid_archs() { 127 | binary="$1" 128 | warn_missing_arch=${2:-true} 129 | # Get architectures for current target binary 130 | binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" 131 | # Intersect them with the architectures we are building for 132 | intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" 133 | # If there are no archs supported by this binary then warn the user 134 | if [[ -z "$intersected_archs" ]]; then 135 | if [[ "$warn_missing_arch" == "true" ]]; then 136 | echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." 137 | fi 138 | STRIP_BINARY_RETVAL=1 139 | return 140 | fi 141 | stripped="" 142 | for arch in $binary_archs; do 143 | if ! [[ "${ARCHS}" == *"$arch"* ]]; then 144 | # Strip non-valid architectures in-place 145 | lipo -remove "$arch" -output "$binary" "$binary" 146 | stripped="$stripped $arch" 147 | fi 148 | done 149 | if [[ "$stripped" ]]; then 150 | echo "Stripped $binary of architectures:$stripped" 151 | fi 152 | STRIP_BINARY_RETVAL=0 153 | } 154 | 155 | # Copies the bcsymbolmap files of a vendored framework 156 | install_bcsymbolmap() { 157 | local bcsymbolmap_path="$1" 158 | local destination="${BUILT_PRODUCTS_DIR}" 159 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}"" 160 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}" 161 | } 162 | 163 | # Signs a framework with the provided identity 164 | code_sign_if_enabled() { 165 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY:-}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 166 | # Use the current code_sign_identity 167 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 168 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'" 169 | 170 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 171 | code_sign_cmd="$code_sign_cmd &" 172 | fi 173 | echo "$code_sign_cmd" 174 | eval "$code_sign_cmd" 175 | fi 176 | } 177 | 178 | if [[ "$CONFIGURATION" == "Debug" ]]; then 179 | install_framework "${BUILT_PRODUCTS_DIR}/SuperTokensIOS/SuperTokensIOS.framework" 180 | fi 181 | if [[ "$CONFIGURATION" == "Release" ]]; then 182 | install_framework "${BUILT_PRODUCTS_DIR}/SuperTokensIOS/SuperTokensIOS.framework" 183 | fi 184 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 185 | wait 186 | fi 187 | -------------------------------------------------------------------------------- /SuperTokensIOS/Classes/SuperTokens.swift: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2020, VRAI Labs and/or its affiliates. All rights reserved. 2 | * 3 | * This software is licensed under the Apache License, Version 2.0 (the 4 | * "License") as published by the Apache Software Foundation. 5 | * 6 | * You may not use this file except in compliance with the License. You may 7 | * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | * License for the specific language governing permissions and limitations 13 | * under the License. 14 | */ 15 | 16 | import Foundation 17 | 18 | public enum EventType { 19 | case SIGN_OUT 20 | case REFRESH_SESSION 21 | case SESSION_CREATED 22 | case ACCESS_TOKEN_PAYLOAD_UPDATED 23 | case UNAUTHORISED 24 | } 25 | 26 | public enum APIAction { 27 | case SIGN_OUT 28 | case REFRESH_SESSION 29 | } 30 | 31 | public class SuperTokens { 32 | static var sessionExpiryStatusCode = 401 33 | static var isInitCalled = false 34 | static var refreshTokenUrl: String = "" 35 | static var signOutUrl: String = "" 36 | static var rid: String = "" 37 | static var config: NormalisedInputType? = nil 38 | 39 | 40 | internal static func resetForTests() { 41 | FrontToken.removeToken() 42 | AntiCSRF.removeToken() 43 | SuperTokens.isInitCalled = false 44 | Utils.setToken(tokenType: .access, value: "") 45 | Utils.setToken(tokenType: .refresh, value: "") 46 | FrontToken.setItem(frontToken: "remove") 47 | } 48 | 49 | public static func initialize(apiDomain: String, apiBasePath: String? = nil, sessionExpiredStatusCode: Int? = nil, sessionTokenBackendDomain: String? = nil, maxRetryAttemptsForSessionRefresh: Int? = nil, tokenTransferMethod: SuperTokensTokenTransferMethod? = nil, userDefaultsSuiteName: String? = nil, eventHandler: ((EventType) -> Void)? = nil, preAPIHook: ((APIAction, URLRequest) -> URLRequest)? = nil, postAPIHook: ((APIAction, URLRequest, URLResponse?) -> Void)? = nil) throws { 50 | if SuperTokens.isInitCalled { 51 | return; 52 | } 53 | 54 | SuperTokens.config = try NormalisedInputType.normaliseInputType(apiDomain: apiDomain, apiBasePath: apiBasePath, sessionExpiredStatusCode: sessionExpiredStatusCode, maxRetryAttemptsForSessionRefresh: maxRetryAttemptsForSessionRefresh, sessionTokenBackendDomain: sessionTokenBackendDomain, tokenTransferMethod: tokenTransferMethod, eventHandler: eventHandler, preAPIHook: preAPIHook, postAPIHook: postAPIHook, userDefaultsSuiteName: userDefaultsSuiteName) 55 | 56 | guard let _config: NormalisedInputType = SuperTokens.config else { 57 | throw SuperTokensError.initError(message: "Error initialising SuperTokens") 58 | } 59 | 60 | SuperTokens.refreshTokenUrl = _config.apiDomain + _config.apiBasePath + "/session/refresh" 61 | SuperTokens.signOutUrl = _config.apiDomain + _config.apiBasePath + "/signout" 62 | SuperTokens.rid = "session" 63 | SuperTokens.isInitCalled = true 64 | } 65 | 66 | public static func doesSessionExist() -> Bool { 67 | let tokenInfo = FrontToken.getToken() 68 | 69 | if tokenInfo == nil { 70 | return false 71 | } 72 | 73 | let currentTimeInMillis: Int = Int(Date().timeIntervalSince1970 * 1000) 74 | 75 | if let accessTokenExpiry: Int = tokenInfo!["ate"] as? Int, accessTokenExpiry < currentTimeInMillis { 76 | let executionSemaphore = DispatchSemaphore(value: 0) 77 | var shouldRetry: Bool = false 78 | var error: Error? 79 | let preRequestLocalSessionState = Utils.getLocalSessionState() 80 | 81 | SuperTokensURLProtocol.onUnauthorisedResponse(preRequestLocalSessionState: preRequestLocalSessionState, callback: { unauthResponse in 82 | 83 | if unauthResponse.status == .API_ERROR { 84 | error = unauthResponse.error 85 | } 86 | 87 | shouldRetry = unauthResponse.status == .RETRY 88 | executionSemaphore.signal() 89 | 90 | }) 91 | 92 | executionSemaphore.wait() 93 | 94 | // Here we dont throw the error and instead return false, because 95 | // otherwise users would have to use a try catch just to call doesSessionExist 96 | if error != nil { 97 | return false 98 | } 99 | 100 | return shouldRetry 101 | } 102 | 103 | return true 104 | } 105 | 106 | public static func signOut(completionHandler: @escaping (Error?) -> Void) { 107 | if !doesSessionExist() { 108 | SuperTokens.config!.eventHandler(.SIGN_OUT) 109 | completionHandler(nil) 110 | return 111 | } 112 | 113 | guard let url: URL = URL(string: SuperTokens.signOutUrl) else { 114 | completionHandler(SuperTokensError.initError(message: "Please provide a valid apiDomain and apiBasePath")) 115 | return 116 | } 117 | 118 | let sessionConfiguration: URLSessionConfiguration = URLSessionConfiguration.default 119 | sessionConfiguration.protocolClasses?.insert(SuperTokensURLProtocol.self, at: 0) 120 | let customSession = URLSession(configuration: sessionConfiguration) 121 | 122 | var signOutRequest = URLRequest(url: url) 123 | signOutRequest.httpMethod = "POST" 124 | signOutRequest.addValue(SuperTokens.rid, forHTTPHeaderField: "rid") 125 | 126 | signOutRequest = SuperTokens.config!.preAPIHook(.SIGN_OUT, signOutRequest) 127 | 128 | let executionSemaphore = DispatchSemaphore(value: 0) 129 | 130 | customSession.dataTask(with: signOutRequest, completionHandler: { 131 | data, response, error in 132 | 133 | if let httpResponse: HTTPURLResponse = response as? HTTPURLResponse { 134 | if httpResponse.statusCode == SuperTokens.config!.sessionExpiredStatusCode { 135 | // refresh must have already sent session expiry event 136 | executionSemaphore.signal() 137 | return 138 | } 139 | 140 | if httpResponse.statusCode >= 300 { 141 | completionHandler(SuperTokensError.apiError(message: "Sign out failed with response code \(httpResponse.statusCode)")) 142 | executionSemaphore.signal() 143 | return 144 | } 145 | 146 | SuperTokens.config!.postAPIHook(.SIGN_OUT, signOutRequest, response) 147 | 148 | if let _data: Data = data, let jsonResponse: SignOutResponse = try? JSONDecoder().decode(SignOutResponse.self, from: _data) { 149 | if jsonResponse.status == "GENERAL_ERROR" { 150 | completionHandler(SuperTokensError.generalError(message: jsonResponse.message!)) 151 | executionSemaphore.signal() 152 | } else { 153 | completionHandler(nil) 154 | executionSemaphore.signal() 155 | } 156 | } else { 157 | completionHandler(SuperTokensError.apiError(message: "Invalid sign out response")) 158 | executionSemaphore.signal() 159 | } 160 | } else { 161 | completionHandler(nil) 162 | executionSemaphore.signal() 163 | } 164 | 165 | // we do not send an event here since it's triggered in fireSessionUpdateEventsIfNecessary. 166 | }).resume() 167 | } 168 | 169 | public static func attemptRefreshingSession() throws -> Bool { 170 | if !SuperTokens.isInitCalled { 171 | throw SuperTokensError.initError(message: "Init function not called") 172 | } 173 | 174 | let preRequestLocalSessionState = Utils.getLocalSessionState() 175 | var error: Error? 176 | let executionSemaphore = DispatchSemaphore(value: 0) 177 | var shouldRetry: Bool = false 178 | 179 | SuperTokensURLProtocol.onUnauthorisedResponse(preRequestLocalSessionState: preRequestLocalSessionState, callback: { 180 | unauthResponse in 181 | 182 | if unauthResponse.status == .API_ERROR { 183 | error = unauthResponse.error 184 | } 185 | 186 | shouldRetry = unauthResponse.status == .RETRY 187 | executionSemaphore.signal() 188 | }) 189 | 190 | executionSemaphore.wait() 191 | 192 | if error != nil { 193 | throw error! 194 | } 195 | 196 | return shouldRetry 197 | } 198 | 199 | public static func getUserId() throws -> String { 200 | guard let frontToken: [String: Any] = FrontToken.getToken(), let userId: String = frontToken["uid"] as? String else { 201 | throw SuperTokensError.illegalAccess(message: "No session exists") 202 | } 203 | 204 | return userId 205 | } 206 | 207 | public static func getAccessTokenPayloadSecurely() throws -> [String: Any] { 208 | guard let frontToken: [String: Any] = FrontToken.getToken(), let accessTokenExpiry: Int = frontToken["ate"] as? Int, let userPayload: [String: Any] = frontToken["up"] as? [String: Any] else { 209 | throw SuperTokensError.illegalAccess(message: "No session exists") 210 | } 211 | 212 | if accessTokenExpiry < Int(Date().timeIntervalSince1970 * 1000) { 213 | let retry = try SuperTokens.attemptRefreshingSession() 214 | 215 | if retry { 216 | return try getAccessTokenPayloadSecurely() 217 | } else { 218 | throw SuperTokensError.illegalAccess(message: "Could not refresh session") 219 | } 220 | } 221 | 222 | return userPayload 223 | } 224 | 225 | public static func getAccessToken() -> String? { 226 | if doesSessionExist() { 227 | return Utils.getTokenForHeaderAuth(tokenType: .access) 228 | } 229 | 230 | return nil 231 | } 232 | } 233 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020, VRAI Labs and/or its affiliates. All rights reserved. 2 | 3 | This software is licensed under the Apache License, Version 2.0 (the 4 | "License") as published by the Apache Software Foundation. 5 | 6 | You may not use this software except in compliance with the License. A copy 7 | of the License is available below the line. 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | License for the specific language governing permissions and limitations 13 | under the License. 14 | 15 | ------------------------------------------------------------------------------- 16 | Apache License 17 | Version 2.0, January 2004 18 | http://www.apache.org/licenses/ 19 | 20 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 21 | 22 | 1. Definitions. 23 | 24 | "License" shall mean the terms and conditions for use, reproduction, 25 | and distribution as defined by Sections 1 through 9 of this document. 26 | 27 | "Licensor" shall mean the copyright owner or entity authorized by 28 | the copyright owner that is granting the License. 29 | 30 | "Legal Entity" shall mean the union of the acting entity and all 31 | other entities that control, are controlled by, or are under common 32 | control with that entity. For the purposes of this definition, 33 | "control" means (i) the power, direct or indirect, to cause the 34 | direction or management of such entity, whether by contract or 35 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 36 | outstanding shares, or (iii) beneficial ownership of such entity. 37 | 38 | "You" (or "Your") shall mean an individual or Legal Entity 39 | exercising permissions granted by this License. 40 | 41 | "Source" form shall mean the preferred form for making modifications, 42 | including but not limited to software source code, documentation 43 | source, and configuration files. 44 | 45 | "Object" form shall mean any form resulting from mechanical 46 | transformation or translation of a Source form, including but 47 | not limited to compiled object code, generated documentation, 48 | and conversions to other media types. 49 | 50 | "Work" shall mean the work of authorship, whether in Source or 51 | Object form, made available under the License, as indicated by a 52 | copyright notice that is included in or attached to the work 53 | (an example is provided in the Appendix below). 54 | 55 | "Derivative Works" shall mean any work, whether in Source or Object 56 | form, that is based on (or derived from) the Work and for which the 57 | editorial revisions, annotations, elaborations, or other modifications 58 | represent, as a whole, an original work of authorship. For the purposes 59 | of this License, Derivative Works shall not include works that remain 60 | separable from, or merely link (or bind by name) to the interfaces of, 61 | the Work and Derivative Works thereof. 62 | 63 | "Contribution" shall mean any work of authorship, including 64 | the original version of the Work and any modifications or additions 65 | to that Work or Derivative Works thereof, that is intentionally 66 | submitted to Licensor for inclusion in the Work by the copyright owner 67 | or by an individual or Legal Entity authorized to submit on behalf of 68 | the copyright owner. For the purposes of this definition, "submitted" 69 | means any form of electronic, verbal, or written communication sent 70 | to the Licensor or its representatives, including but not limited to 71 | communication on electronic mailing lists, source code control systems, 72 | and issue tracking systems that are managed by, or on behalf of, the 73 | Licensor for the purpose of discussing and improving the Work, but 74 | excluding communication that is conspicuously marked or otherwise 75 | designated in writing by the copyright owner as "Not a Contribution." 76 | 77 | "Contributor" shall mean Licensor and any individual or Legal Entity 78 | on behalf of whom a Contribution has been received by Licensor and 79 | subsequently incorporated within the Work. 80 | 81 | 2. Grant of Copyright License. Subject to the terms and conditions of 82 | this License, each Contributor hereby grants to You a perpetual, 83 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 84 | copyright license to reproduce, prepare Derivative Works of, 85 | publicly display, publicly perform, sublicense, and distribute the 86 | Work and such Derivative Works in Source or Object form. 87 | 88 | 3. Grant of Patent License. Subject to the terms and conditions of 89 | this License, each Contributor hereby grants to You a perpetual, 90 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 91 | (except as stated in this section) patent license to make, have made, 92 | use, offer to sell, sell, import, and otherwise transfer the Work, 93 | where such license applies only to those patent claims licensable 94 | by such Contributor that are necessarily infringed by their 95 | Contribution(s) alone or by combination of their Contribution(s) 96 | with the Work to which such Contribution(s) was submitted. If You 97 | institute patent litigation against any entity (including a 98 | cross-claim or counterclaim in a lawsuit) alleging that the Work 99 | or a Contribution incorporated within the Work constitutes direct 100 | or contributory patent infringement, then any patent licenses 101 | granted to You under this License for that Work shall terminate 102 | as of the date such litigation is filed. 103 | 104 | 4. Redistribution. You may reproduce and distribute copies of the 105 | Work or Derivative Works thereof in any medium, with or without 106 | modifications, and in Source or Object form, provided that You 107 | meet the following conditions: 108 | 109 | (a) You must give any other recipients of the Work or 110 | Derivative Works a copy of this License; and 111 | 112 | (b) You must cause any modified files to carry prominent notices 113 | stating that You changed the files; and 114 | 115 | (c) You must retain, in the Source form of any Derivative Works 116 | that You distribute, all copyright, patent, trademark, and 117 | attribution notices from the Source form of the Work, 118 | excluding those notices that do not pertain to any part of 119 | the Derivative Works; and 120 | 121 | (d) If the Work includes a "NOTICE" text file as part of its 122 | distribution, then any Derivative Works that You distribute must 123 | include a readable copy of the attribution notices contained 124 | within such NOTICE file, excluding those notices that do not 125 | pertain to any part of the Derivative Works, in at least one 126 | of the following places: within a NOTICE text file distributed 127 | as part of the Derivative Works; within the Source form or 128 | documentation, if provided along with the Derivative Works; or, 129 | within a display generated by the Derivative Works, if and 130 | wherever such third-party notices normally appear. The contents 131 | of the NOTICE file are for informational purposes only and 132 | do not modify the License. You may add Your own attribution 133 | notices within Derivative Works that You distribute, alongside 134 | or as an addendum to the NOTICE text from the Work, provided 135 | that such additional attribution notices cannot be construed 136 | as modifying the License. 137 | 138 | You may add Your own copyright statement to Your modifications and 139 | may provide additional or different license terms and conditions 140 | for use, reproduction, or distribution of Your modifications, or 141 | for any such Derivative Works as a whole, provided Your use, 142 | reproduction, and distribution of the Work otherwise complies with 143 | the conditions stated in this License. 144 | 145 | 5. Submission of Contributions. Unless You explicitly state otherwise, 146 | any Contribution intentionally submitted for inclusion in the Work 147 | by You to the Licensor shall be under the terms and conditions of 148 | this License, without any additional terms or conditions. 149 | Notwithstanding the above, nothing herein shall supersede or modify 150 | the terms of any separate license agreement you may have executed 151 | with Licensor regarding such Contributions. 152 | 153 | 6. Trademarks. This License does not grant permission to use the trade 154 | names, trademarks, service marks, or product names of the Licensor, 155 | except as required for reasonable and customary use in describing the 156 | origin of the Work and reproducing the content of the NOTICE file. 157 | 158 | 7. Disclaimer of Warranty. Unless required by applicable law or 159 | agreed to in writing, Licensor provides the Work (and each 160 | Contributor provides its Contributions) on an "AS IS" BASIS, 161 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 162 | implied, including, without limitation, any warranties or conditions 163 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 164 | PARTICULAR PURPOSE. You are solely responsible for determining the 165 | appropriateness of using or redistributing the Work and assume any 166 | risks associated with Your exercise of permissions under this License. 167 | 168 | 8. Limitation of Liability. In no event and under no legal theory, 169 | whether in tort (including negligence), contract, or otherwise, 170 | unless required by applicable law (such as deliberate and grossly 171 | negligent acts) or agreed to in writing, shall any Contributor be 172 | liable to You for damages, including any direct, indirect, special, 173 | incidental, or consequential damages of any character arising as a 174 | result of this License or out of the use or inability to use the 175 | Work (including but not limited to damages for loss of goodwill, 176 | work stoppage, computer failure or malfunction, or any and all 177 | other commercial damages or losses), even if such Contributor 178 | has been advised of the possibility of such damages. 179 | 180 | 9. Accepting Warranty or Additional Liability. While redistributing 181 | the Work or Derivative Works thereof, You may choose to offer, 182 | and charge a fee for, acceptance of support, warranty, indemnity, 183 | or other liability obligations and/or rights consistent with this 184 | License. However, in accepting such obligations, You may act only 185 | on Your own behalf and on Your sole responsibility, not on behalf 186 | of any other Contributor, and only if You agree to indemnify, 187 | defend, and hold each Contributor harmless for any liability 188 | incurred by, or claims asserted against, such Contributor by reason 189 | of your accepting any such warranty or additional liability. 190 | 191 | END OF TERMS AND CONDITIONS 192 | -------------------------------------------------------------------------------- /testHelpers/testapp/Pods/Target Support Files/Pods-SuperTokensSession_Example/Pods-SuperTokensSession_Example-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## SuperTokensIOS 5 | 6 | Copyright (c) 2020, VRAI Labs and/or its affiliates. All rights reserved. 7 | 8 | This software is licensed under the Apache License, Version 2.0 (the 9 | "License") as published by the Apache Software Foundation. 10 | 11 | You may not use this software except in compliance with the License. A copy 12 | of the License is available below the line. 13 | 14 | Unless required by applicable law or agreed to in writing, software 15 | distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 16 | WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 17 | License for the specific language governing permissions and limitations 18 | under the License. 19 | 20 | ------------------------------------------------------------------------------- 21 | Apache License 22 | Version 2.0, January 2004 23 | http://www.apache.org/licenses/ 24 | 25 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 26 | 27 | 1. Definitions. 28 | 29 | "License" shall mean the terms and conditions for use, reproduction, 30 | and distribution as defined by Sections 1 through 9 of this document. 31 | 32 | "Licensor" shall mean the copyright owner or entity authorized by 33 | the copyright owner that is granting the License. 34 | 35 | "Legal Entity" shall mean the union of the acting entity and all 36 | other entities that control, are controlled by, or are under common 37 | control with that entity. For the purposes of this definition, 38 | "control" means (i) the power, direct or indirect, to cause the 39 | direction or management of such entity, whether by contract or 40 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 41 | outstanding shares, or (iii) beneficial ownership of such entity. 42 | 43 | "You" (or "Your") shall mean an individual or Legal Entity 44 | exercising permissions granted by this License. 45 | 46 | "Source" form shall mean the preferred form for making modifications, 47 | including but not limited to software source code, documentation 48 | source, and configuration files. 49 | 50 | "Object" form shall mean any form resulting from mechanical 51 | transformation or translation of a Source form, including but 52 | not limited to compiled object code, generated documentation, 53 | and conversions to other media types. 54 | 55 | "Work" shall mean the work of authorship, whether in Source or 56 | Object form, made available under the License, as indicated by a 57 | copyright notice that is included in or attached to the work 58 | (an example is provided in the Appendix below). 59 | 60 | "Derivative Works" shall mean any work, whether in Source or Object 61 | form, that is based on (or derived from) the Work and for which the 62 | editorial revisions, annotations, elaborations, or other modifications 63 | represent, as a whole, an original work of authorship. For the purposes 64 | of this License, Derivative Works shall not include works that remain 65 | separable from, or merely link (or bind by name) to the interfaces of, 66 | the Work and Derivative Works thereof. 67 | 68 | "Contribution" shall mean any work of authorship, including 69 | the original version of the Work and any modifications or additions 70 | to that Work or Derivative Works thereof, that is intentionally 71 | submitted to Licensor for inclusion in the Work by the copyright owner 72 | or by an individual or Legal Entity authorized to submit on behalf of 73 | the copyright owner. For the purposes of this definition, "submitted" 74 | means any form of electronic, verbal, or written communication sent 75 | to the Licensor or its representatives, including but not limited to 76 | communication on electronic mailing lists, source code control systems, 77 | and issue tracking systems that are managed by, or on behalf of, the 78 | Licensor for the purpose of discussing and improving the Work, but 79 | excluding communication that is conspicuously marked or otherwise 80 | designated in writing by the copyright owner as "Not a Contribution." 81 | 82 | "Contributor" shall mean Licensor and any individual or Legal Entity 83 | on behalf of whom a Contribution has been received by Licensor and 84 | subsequently incorporated within the Work. 85 | 86 | 2. Grant of Copyright License. Subject to the terms and conditions of 87 | this License, each Contributor hereby grants to You a perpetual, 88 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 89 | copyright license to reproduce, prepare Derivative Works of, 90 | publicly display, publicly perform, sublicense, and distribute the 91 | Work and such Derivative Works in Source or Object form. 92 | 93 | 3. Grant of Patent License. Subject to the terms and conditions of 94 | this License, each Contributor hereby grants to You a perpetual, 95 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 96 | (except as stated in this section) patent license to make, have made, 97 | use, offer to sell, sell, import, and otherwise transfer the Work, 98 | where such license applies only to those patent claims licensable 99 | by such Contributor that are necessarily infringed by their 100 | Contribution(s) alone or by combination of their Contribution(s) 101 | with the Work to which such Contribution(s) was submitted. If You 102 | institute patent litigation against any entity (including a 103 | cross-claim or counterclaim in a lawsuit) alleging that the Work 104 | or a Contribution incorporated within the Work constitutes direct 105 | or contributory patent infringement, then any patent licenses 106 | granted to You under this License for that Work shall terminate 107 | as of the date such litigation is filed. 108 | 109 | 4. Redistribution. You may reproduce and distribute copies of the 110 | Work or Derivative Works thereof in any medium, with or without 111 | modifications, and in Source or Object form, provided that You 112 | meet the following conditions: 113 | 114 | (a) You must give any other recipients of the Work or 115 | Derivative Works a copy of this License; and 116 | 117 | (b) You must cause any modified files to carry prominent notices 118 | stating that You changed the files; and 119 | 120 | (c) You must retain, in the Source form of any Derivative Works 121 | that You distribute, all copyright, patent, trademark, and 122 | attribution notices from the Source form of the Work, 123 | excluding those notices that do not pertain to any part of 124 | the Derivative Works; and 125 | 126 | (d) If the Work includes a "NOTICE" text file as part of its 127 | distribution, then any Derivative Works that You distribute must 128 | include a readable copy of the attribution notices contained 129 | within such NOTICE file, excluding those notices that do not 130 | pertain to any part of the Derivative Works, in at least one 131 | of the following places: within a NOTICE text file distributed 132 | as part of the Derivative Works; within the Source form or 133 | documentation, if provided along with the Derivative Works; or, 134 | within a display generated by the Derivative Works, if and 135 | wherever such third-party notices normally appear. The contents 136 | of the NOTICE file are for informational purposes only and 137 | do not modify the License. You may add Your own attribution 138 | notices within Derivative Works that You distribute, alongside 139 | or as an addendum to the NOTICE text from the Work, provided 140 | that such additional attribution notices cannot be construed 141 | as modifying the License. 142 | 143 | You may add Your own copyright statement to Your modifications and 144 | may provide additional or different license terms and conditions 145 | for use, reproduction, or distribution of Your modifications, or 146 | for any such Derivative Works as a whole, provided Your use, 147 | reproduction, and distribution of the Work otherwise complies with 148 | the conditions stated in this License. 149 | 150 | 5. Submission of Contributions. Unless You explicitly state otherwise, 151 | any Contribution intentionally submitted for inclusion in the Work 152 | by You to the Licensor shall be under the terms and conditions of 153 | this License, without any additional terms or conditions. 154 | Notwithstanding the above, nothing herein shall supersede or modify 155 | the terms of any separate license agreement you may have executed 156 | with Licensor regarding such Contributions. 157 | 158 | 6. Trademarks. This License does not grant permission to use the trade 159 | names, trademarks, service marks, or product names of the Licensor, 160 | except as required for reasonable and customary use in describing the 161 | origin of the Work and reproducing the content of the NOTICE file. 162 | 163 | 7. Disclaimer of Warranty. Unless required by applicable law or 164 | agreed to in writing, Licensor provides the Work (and each 165 | Contributor provides its Contributions) on an "AS IS" BASIS, 166 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 167 | implied, including, without limitation, any warranties or conditions 168 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 169 | PARTICULAR PURPOSE. You are solely responsible for determining the 170 | appropriateness of using or redistributing the Work and assume any 171 | risks associated with Your exercise of permissions under this License. 172 | 173 | 8. Limitation of Liability. In no event and under no legal theory, 174 | whether in tort (including negligence), contract, or otherwise, 175 | unless required by applicable law (such as deliberate and grossly 176 | negligent acts) or agreed to in writing, shall any Contributor be 177 | liable to You for damages, including any direct, indirect, special, 178 | incidental, or consequential damages of any character arising as a 179 | result of this License or out of the use or inability to use the 180 | Work (including but not limited to damages for loss of goodwill, 181 | work stoppage, computer failure or malfunction, or any and all 182 | other commercial damages or losses), even if such Contributor 183 | has been advised of the possibility of such damages. 184 | 185 | 9. Accepting Warranty or Additional Liability. While redistributing 186 | the Work or Derivative Works thereof, You may choose to offer, 187 | and charge a fee for, acceptance of support, warranty, indemnity, 188 | or other liability obligations and/or rights consistent with this 189 | License. However, in accepting such obligations, You may act only 190 | on Your own behalf and on Your sole responsibility, not on behalf 191 | of any other Contributor, and only if You agree to indemnify, 192 | defend, and hold each Contributor harmless for any liability 193 | incurred by, or claims asserted against, such Contributor by reason 194 | of your accepting any such warranty or additional liability. 195 | 196 | END OF TERMS AND CONDITIONS 197 | 198 | Generated by CocoaPods - https://cocoapods.org 199 | --------------------------------------------------------------------------------