diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..b181d5a --- /dev/null +++ b/AUTHORS @@ -0,0 +1,15 @@ +# This is the official list of the AppAuth for iOS authors for copyright purposes. +# This file is distinct from the CONTRIBUTORS files. +# See the latter for an explanation. +# Names should be added to this file as: +# Name or Organization +# The email address is not required for organizations. + +Google Inc. +Rebecka Gulliksson +Ping Identity +equinux AG +Craig Lane +Hernan Zalazar +Julien Bodet + diff --git a/AppAuth.podspec b/AppAuth.podspec new file mode 100644 index 0000000..d4191ad --- /dev/null +++ b/AppAuth.podspec @@ -0,0 +1,78 @@ +Pod::Spec.new do |s| + + s.name = "AppAuth" + s.version = "1.4.0" + s.summary = "AppAuth for iOS and macOS is a client SDK for communicating with OAuth 2.0 and OpenID Connect providers." + + s.description = <<-DESC + +AppAuth for iOS and macOS is a client SDK for communicating with [OAuth 2.0] +(https://tools.ietf.org/html/rfc6749) and [OpenID Connect] +(http://openid.net/specs/openid-connect-core-1_0.html) providers. It strives to +directly map the requests and responses of those specifications, while following +the idiomatic style of the implementation language. In addition to mapping the +raw protocol flows, convenience methods are available to assist with common +tasks like performing an action with fresh tokens. + +It follows the OAuth 2.0 for Native Apps best current practice +([RFC 8252](https://tools.ietf.org/html/rfc8252)). + + DESC + + s.homepage = "https://openid.github.io/AppAuth-iOS" + s.license = "Apache License, Version 2.0" + s.authors = { "William Denniss" => "wdenniss@google.com", + "Steven E Wright" => "stevewright@google.com", + "Julien Bodet" => "julien.bodet92@gmail.com" + } + + # Note: While watchOS and tvOS are specified here, only iOS and macOS have + # UI implementations of the authorization service. You can use the + # classes of AppAuth with tokens on watchOS and tvOS, but currently the + # library won't help you obtain authorization grants on those platforms. + + s.ios.deployment_target = "7.0" + s.osx.deployment_target = "10.9" + s.watchos.deployment_target = "2.0" + s.tvos.deployment_target = "9.0" + + s.source = { :git => "https://github.com/openid/AppAuth-iOS.git", :tag => s.version } + s.requires_arc = true + + # Subspec for the core AppAuth library classes only, suitable for extensions. + s.subspec 'Core' do |core| + core.source_files = "Source/AppAuthCore.h", "Source/AppAuthCore/*.{h,m}" + end + + # Subspec for the full AppAuth library, including platform-dependant external user agents. + s.subspec 'ExternalUserAgent' do |externalUserAgent| + externalUserAgent.dependency 'AppAuth/Core' + + externalUserAgent.source_files = "Source/AppAuth.h", "Source/AppAuth/*.{h,m}" + + # iOS + externalUserAgent.ios.source_files = "Source/AppAuth/iOS/**/*.{h,m}" + externalUserAgent.ios.deployment_target = "7.0" + externalUserAgent.ios.frameworks = "SafariServices" + externalUserAgent.ios.weak_frameworks = "AuthenticationServices" + + # macOS + externalUserAgent.osx.source_files = "Source/AppAuth/macOS/**/*.{h,m}" + externalUserAgent.osx.deployment_target = '10.9' + end + + s.subspec 'EnterpriseUserAgent' do |enterpriseUserAgent| + enterpriseUserAgent.dependency 'AppAuth/Core' + + enterpriseUserAgent.ios.source_files = "Source/AppAuthEnterpriseUserAgent.h", "Source/AppAuthEnterpriseUserAgent/iOS/**/*.{h,m}" + enterpriseUserAgent.ios.deployment_target = "7.0" + end + + # Subspec for the full AppAuth library, including platform-dependant external user agents. + s.subspec 'TV' do |tv| + tv.source_files = "Source/AppAuthTV.h", "Source/AppAuthTV/*.{h,m}" + tv.dependency 'AppAuth/Core' + end + + s.default_subspecs = 'Core', 'ExternalUserAgent' +end diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..7ebb29d --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,201 @@ +# AppAuth for iOS and macOS Changelog + +## 1.4.0 + +### Added + +1. Support for Swift Package Manager + +## 1.3.1 + +### Fixes + +1. Removed `UIWebView` reference in comment + +## 1.3.0 + +### Notable Changes + +1. Support for Mac Catalyst + +## 1.2.0 + +### Notable Changes + +1. Support for iOS 13 + +## 1.1.0 + +### Notable Changes + +1. [OpenID Connect RP-Initiated Logout](http://openid.net/specs/openid-connect-session-1_0.html#RPLogout) implemented +2. Added logic for the `azp` claim + +### Fixes + +1. Scheme comparison for redirects is now case insensitive +2. Improved error handling during discovery when a non-JSON document + is encountered. + +## 1.0.0 + +1.0.0! 🎉 + +### Notable Changes + +1. **All deprecated APIs removed.** Please ensure your code builds on + version 0.95.0 with no deprecation warnings before upgrading! + Notably, if you started with a version of AppAuth prior to 0.93.0 + you will need to follow the instructions in + [Upgrading to 0.93.0](#upgrading-to-0930) +2. Updated for iOS 12, and Xcode 10. **Xcode 10 is now required.** + NB. per policy, AppAuth supports many older versions of iOS and + macOS, but only the current Xcode toolchain. + If you need to stay on old versions of Xcode for some reason, stay + on the pre-1.0 releases. +3. macOS 32-bit support removed. If you need this support, stay on the + pre-1.0 releases. +4. `AppAuth/Core` subspec, and AppAuthCore Framework added to support + iOS extensions. + +## 1.0.0.beta2 (2018-09-27) + +### Notable Changes + +1. `AppAuth/Core` subspec, and AppAuthCore Framework added to support + iOS extensions. + +## 1.0.0.beta1 (2018-09-27) + +First 1.0.0 beta! HEAD is now tracking changes for the 1.0.0 release. +The `pre-1.0` branch was cut prior to the breaking changes for 1.0.0, +bug fixes for critical issues may be backported for a time. + +### Notable Changes + +1. **All deprecated APIs removed.** Please ensure your code builds on + version 0.95.0 with no deprecation warnings before upgrading! + Notably, if you started with a version of AppAuth prior to 0.93.0 + you will need to follow the instructions in + [Upgrading to 0.93.0](#upgrading-to-0930) +2. Updated for iOS 12, and Xcode 10. **Xcode 10 is now required.** + NB. per policy, AppAuth supports many older versions of iOS and + macOS, but only the current Xcode toolchain. + If you need to stay on old versions of Xcode for some reason, stay + on the pre-1.0 releases. +3. macOS 32-bit support removed. If you need this support, stay on the + pre-1.0 releases. + +### Fixes + +1. All fixes in the 0.95.0 release are incorporated in this release. + +## 0.95.0 (2018-09-27) + +### Fixes + +1. `x-www-form-urlencoded` encoding and decoding should be 100% + spec compliant now, previously the `+` character was not decoded as + 0x20 space. https://github.com/openid/AppAuth-iOS/pull/291 + +2. `scope` no longer sent during token refresh (was redundant) + https://github.com/openid/AppAuth-iOS/pull/301 + +## 0.94.0 (2018-07-13) + +### Fixes +1. `form-urlencode` client ID and client secret in Authorization header + +### Added + +1. Samples have icons now! +2. Output trace logs by defining `_APPAUTHTRACE` + +## 0.93.0 (2018-06-26) + +### Notable Changes + +1. Implements OpenID Connect (ID Token handling) and the OpenID Connect + RP Certification test suite. + https://github.com/openid/AppAuth-iOS/pull/101 + +2. The `OIDAuthorizationUICoordinator` pattern was genericized to + support non-authorization external user-agent flows like logout + (though none are directly implemented by AppAuth, yet). + `OIDAuthorizationUICoordinator*` classes renamed to + `OIDExternalUserAgent*`. + https://github.com/openid/AppAuth-iOS/pull/196 + https://github.com/openid/AppAuth-iOS/pull/212 + See [Upgrading to 0.93.0](#upgrading-to-0930). + +3. Added custom browser support on iOS. Provides several + convenience implementations of alternative external user-agents on + iOS such as Chrome and Firefox. These are intended for + **enterprise use only**, where the app developers have greater + control over the operating environment and have special requirements + that require a custom browser like Chrome. + See the [code example](https://github.com/openid/AppAuth-iOS/issues/200#issuecomment-364610027). + https://github.com/openid/AppAuth-iOS/issues/200 + https://github.com/openid/AppAuth-iOS/pull/201 + +### Upgrading to 0.93.0 + +0.93.0 deprecates several methods. To update your code to avoid the +deprecated methods (which will be required for the 1.0.0 release), +you will need to make changes. + +If you implemented your own `OIDAuthorizationUICoordinator`, or called +the methods which accepted a `UICoordinator` instance, you will need to +update to the new method names. See the deprecation error messages +for the new methods to use in those cases. + +Most users who are using the convenience methods of AppAuth will only +need to make the following 3 minor changes to their AppDelegate: + +#### Import: + +Change +```objc +@protocol OIDAuthorizationFlowSession; +``` +to +```objc +@protocol OIDExternalUserAgentSession; +``` + +#### Property: + +Change +```objc +@property(nonatomic, strong, nullable) id currentAuthorizationFlow; +``` +to +```objc +@property(nonatomic, strong, nullable) idcurrentAuthorizationFlow; +``` + +#### Implementation of `-(BOOL)application:openURL:options:` +Change +```objc +if ([_currentAuthorizationFlow resumeAuthorizationFlowWithURL:url]) { +``` +to +```objc +if ([_currentAuthorizationFlow resumeExternalUserAgentFlowWithURL:url]) { +``` + +See also the changes made to the sample which you can copy: +https://github.com/openid/AppAuth-iOS/commit/619bb7c7d5f83cc2ed19380d425ca8afa279644c?diff=unified + + +## 0.92.0 (2018-01-05) + +### Improvements + +1. Added an official Swift sample, and included Swift testing in the + continuous integration tests. + +## Pre 0.92.0 + +No changelog entries exist for changes prior to 2018, please review the +[git history](https://github.com/openid/AppAuth-iOS/commits/0.91.0). diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..51537d3 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,59 @@ +# Contributing to AppAuth + +All contributions to AppAuth for iOS are welcome! + +Note that as this library is planned to be used in high-profile production code, +we insist on a very high standards for the code and design, but don't feel shy: +discuss your plans over +[GitHub Issues](https://github.com/openid/AppAuth-iOS/issues) and the +[mailing list](http://lists.openid.net/mailman/listinfo/openid-specs-ab), and +send in those pull requests! + +# Signing the Agreements + +In order to contribute to this project, you need to execute two legal agreements +that cover your contributions. Pull requests from users who have not signed +these agreements will not be merged. + +## Execute the Contributor License Agreement (CLA) + +1. Visit http://openid.net/contribution-license-agreement/ +2. Tap *Execute OpenID Foundation Contribution License Agreement* for the + version relevant to you (Individual or Corporate). +3. Follow the instructions to sign the agreement. + +## Execute the Working Group Contribution Agreement + +In addition to the Code License Agreement, the OpenID Foundation also requires +a working group contribution agreement to cover any contributions you may make +towards the OpenID Connect spec itself (e.g. in comments, bug reports, feature +requests). + +1. Visit http://openid.net/intellectual-property/ +2. Tap *Execute Contributor Agreement By Electronic Signature* in the box + marked *Resources*. +3. Follow the instructions to sign the document, state `OpenID AB/Connect` as + the Initial Working Group + +# Making a Pull Request + +## Before you Start + +Before you work on a big new feature, get in touch to make sure that your work +is inline with the direction of the project and get input on your architecture. +You can file an [Issue](https://github.com/openid/AppAuth-iOS/issues) +discussing your proposal, or email the +[list](http://lists.openid.net/mailman/listinfo/openid-specs-ab). + +## Coding Standards + +The AppAuth library follows the +[Google Coding Style](https://google.github.io/styleguide/objcguide.xml) for +the Objective-C language. Please review your own code for adherence to the +standard. + +## Pull Request Reviews + +All pull requests, even by members who have repository write access need to be +reviewed and marked as "LGTM" before they will be merged. + diff --git a/CONTRIBUTORS b/CONTRIBUTORS new file mode 100644 index 0000000..bf6f154 --- /dev/null +++ b/CONTRIBUTORS @@ -0,0 +1,22 @@ +# People who have agreed to one of the CLAs and can contribute patches. +# The AUTHORS file lists the copyright holders; this file +# lists people. For example, Google employees are listed here +# but not in AUTHORS, because Google holds the copyright. +# +# https://developers.google.com/open-source/cla/individual +# https://developers.google.com/open-source/cla/corporate +# +# Names should be added to this file as: +# Name + +Steven E Wright +William Denniss +Iain McGinniss +Xiangtian Dai +Rebecka Gulliksson +David Waite +Craig Lane https://github.com/ProjectLane +Hernan Zalazar https://github.com/hzalaz +Joseph Heenan https://github.com/jogu +Julien Bodet https://github.com/julienbodet +Tobias Schröpf https://github.com/schroepf diff --git a/Doxyfile b/Doxyfile new file mode 100644 index 0000000..a6e6485 --- /dev/null +++ b/Doxyfile @@ -0,0 +1,388 @@ +# Doxyfile 1.8.11 + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- +DOXYFILE_ENCODING = UTF-8 +PROJECT_NAME = AppAuth +PROJECT_NUMBER = +PROJECT_BRIEF = +PROJECT_LOGO = +OUTPUT_DIRECTORY = Docs +CREATE_SUBDIRS = NO +ALLOW_UNICODE_NAMES = NO +OUTPUT_LANGUAGE = English +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the +ALWAYS_DETAILED_SEC = NO +INLINE_INHERITED_MEMB = NO +FULL_PATH_NAMES = YES +STRIP_FROM_PATH = +STRIP_FROM_INC_PATH = +SHORT_NAMES = NO +JAVADOC_AUTOBRIEF = NO +QT_AUTOBRIEF = NO +MULTILINE_CPP_IS_BRIEF = NO +INHERIT_DOCS = YES +SEPARATE_MEMBER_PAGES = NO +TAB_SIZE = 4 +# Alias external references. ref: http://stackoverflow.com/a/24076896/72176 +ALIASES += SFSafariViewController="SFSafariViewController" +ALIASES += NSURLSession="NSURLSession" +ALIASES += NSError_userInfo="NSError.userInfo" +ALIASES += NSError_code="NSError.code" +ALIASES += NSError="NSError" +ALIASES += NSHTTPURLResponse="NSHTTPURLResponse" +TCL_SUBST = +OPTIMIZE_OUTPUT_FOR_C = NO +OPTIMIZE_OUTPUT_JAVA = NO +OPTIMIZE_FOR_FORTRAN = NO +OPTIMIZE_OUTPUT_VHDL = NO +EXTENSION_MAPPING = m=Objective-C \ + h=Objective-C +MARKDOWN_SUPPORT = YES +AUTOLINK_SUPPORT = YES +BUILTIN_STL_SUPPORT = NO +CPP_CLI_SUPPORT = NO +SIP_SUPPORT = NO +IDL_PROPERTY_SUPPORT = YES +DISTRIBUTE_GROUP_DOC = NO +GROUP_NESTED_COMPOUNDS = NO +SUBGROUPING = YES +INLINE_GROUPED_CLASSES = NO +INLINE_SIMPLE_STRUCTS = NO +TYPEDEF_HIDES_STRUCT = NO +LOOKUP_CACHE_SIZE = 0 +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- +EXTRACT_ALL = NO +EXTRACT_PRIVATE = NO +EXTRACT_PACKAGE = NO +EXTRACT_STATIC = NO +EXTRACT_LOCAL_CLASSES = YES +EXTRACT_LOCAL_METHODS = NO +EXTRACT_ANON_NSPACES = NO +HIDE_UNDOC_MEMBERS = YES +HIDE_UNDOC_CLASSES = NO +HIDE_FRIEND_COMPOUNDS = NO +HIDE_IN_BODY_DOCS = NO +INTERNAL_DOCS = NO +CASE_SENSE_NAMES = NO +HIDE_SCOPE_NAMES = NO +HIDE_COMPOUND_REFERENCE= NO +SHOW_INCLUDE_FILES = YES +SHOW_GROUPED_MEMB_INC = NO +FORCE_LOCAL_INCLUDES = NO +INLINE_INFO = YES +SORT_MEMBER_DOCS = YES +SORT_BRIEF_DOCS = NO +SORT_MEMBERS_CTORS_1ST = NO +SORT_GROUP_NAMES = NO +SORT_BY_SCOPE_NAME = NO +STRICT_PROTO_MATCHING = NO +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +GENERATE_BUGLIST = YES +GENERATE_DEPRECATEDLIST= YES +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 30 +SHOW_USED_FILES = YES +SHOW_FILES = YES +SHOW_NAMESPACES = YES +FILE_VERSION_FILTER = +LAYOUT_FILE = +CITE_BIB_FILES = +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- +QUIET = NO +WARNINGS = YES +WARN_IF_UNDOCUMENTED = YES +WARN_IF_DOC_ERROR = YES +WARN_NO_PARAMDOC = NO +WARN_AS_ERROR = NO +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- +INPUT = Source +INPUT_ENCODING = UTF-8 +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cpp \ + *.c++ \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.idl \ + *.ddl \ + *.odl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.cs \ + *.d \ + *.php \ + *.php4 \ + *.php5 \ + *.phtml \ + *.inc \ + *.m \ + *.markdown \ + *.md \ + *.mm \ + *.dox \ + *.py \ + *.pyw \ + *.f90 \ + *.f \ + *.for \ + *.tcl \ + *.vhd \ + *.vhdl \ + *.ucf \ + *.qsf \ + *.as \ + *.js +RECURSIVE = YES +EXCLUDE = +EXCLUDE_SYMLINKS = NO +# Excludes .m files +EXCLUDE_PATTERNS = *.m +# Excludes private classes +EXCLUDE_PATTERNS += */LoopbackHTTPServer/* +EXCLUDE_PATTERNS += */OIDURLQueryComponent.h +EXCLUDE_PATTERNS += */OIDFieldMapping.h +# Excludes private categories +EXCLUDE_SYMBOLS = *() +EXAMPLE_PATH = +EXAMPLE_PATTERNS = * +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = +INPUT_FILTER = +FILTER_PATTERNS = +FILTER_SOURCE_FILES = NO +FILTER_SOURCE_PATTERNS = +USE_MDFILE_AS_MAINPAGE = +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- +SOURCE_BROWSER = NO +INLINE_SOURCES = NO +STRIP_CODE_COMMENTS = YES +REFERENCED_BY_RELATION = NO +REFERENCES_RELATION = NO +REFERENCES_LINK_SOURCE = YES +SOURCE_TOOLTIPS = YES +USE_HTAGS = NO +VERBATIM_HEADERS = YES +CLANG_ASSISTED_PARSING = NO +CLANG_OPTIONS = +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- +ALPHABETICAL_INDEX = YES +COLS_IN_ALPHA_INDEX = 5 +IGNORE_PREFIX = +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- +GENERATE_HTML = YES +HTML_OUTPUT = html +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_EXTRA_STYLESHEET = +HTML_EXTRA_FILES = +HTML_COLORSTYLE_HUE = 220 +HTML_COLORSTYLE_SAT = 100 +HTML_COLORSTYLE_GAMMA = 80 +HTML_TIMESTAMP = NO +HTML_DYNAMIC_SECTIONS = NO +HTML_INDEX_NUM_ENTRIES = 100 +GENERATE_DOCSET = NO +DOCSET_FEEDNAME = "Doxygen generated docs" +DOCSET_BUNDLE_ID = org.doxygen.Project +DOCSET_PUBLISHER_ID = org.doxygen.Publisher +DOCSET_PUBLISHER_NAME = Publisher +GENERATE_HTMLHELP = NO +CHM_FILE = +HHC_LOCATION = +GENERATE_CHI = NO +CHM_INDEX_ENCODING = +BINARY_TOC = NO +TOC_EXPAND = NO +GENERATE_QHP = NO +QCH_FILE = +QHP_NAMESPACE = org.doxygen.Project +QHP_VIRTUAL_FOLDER = doc +QHP_CUST_FILTER_NAME = +QHP_CUST_FILTER_ATTRS = +QHP_SECT_FILTER_ATTRS = +QHG_LOCATION = +GENERATE_ECLIPSEHELP = NO +ECLIPSE_DOC_ID = org.doxygen.Project +DISABLE_INDEX = NO +GENERATE_TREEVIEW = NO +ENUM_VALUES_PER_LINE = 4 +TREEVIEW_WIDTH = 250 +EXT_LINKS_IN_WINDOW = NO +FORMULA_FONTSIZE = 10 +FORMULA_TRANSPARENT = YES +USE_MATHJAX = NO +MATHJAX_FORMAT = HTML-CSS +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest +MATHJAX_EXTENSIONS = +MATHJAX_CODEFILE = +SEARCHENGINE = YES +SERVER_BASED_SEARCH = NO +EXTERNAL_SEARCH = NO +SEARCHENGINE_URL = +SEARCHDATA_FILE = searchdata.xml +EXTERNAL_SEARCH_ID = +EXTRA_SEARCH_MAPPINGS = +#--------------------------------------------------------------------------- +# Configuration options related to the LaTeX output +#--------------------------------------------------------------------------- +GENERATE_LATEX = YES +LATEX_OUTPUT = latex +LATEX_CMD_NAME = latex +MAKEINDEX_CMD_NAME = makeindex +COMPACT_LATEX = NO +PAPER_TYPE = a4 +EXTRA_PACKAGES = +LATEX_HEADER = +LATEX_FOOTER = +LATEX_EXTRA_STYLESHEET = +LATEX_EXTRA_FILES = +PDF_HYPERLINKS = YES +USE_PDFLATEX = YES +LATEX_BATCHMODE = NO +LATEX_HIDE_INDICES = NO +LATEX_SOURCE_CODE = NO +LATEX_BIB_STYLE = plain +LATEX_TIMESTAMP = NO +#--------------------------------------------------------------------------- +# Configuration options related to the RTF output +#--------------------------------------------------------------------------- +GENERATE_RTF = NO +RTF_OUTPUT = rtf +COMPACT_RTF = NO +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = +RTF_SOURCE_CODE = NO +#--------------------------------------------------------------------------- +# Configuration options related to the man page output +#--------------------------------------------------------------------------- +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_SUBDIR = +MAN_LINKS = NO +#--------------------------------------------------------------------------- +# Configuration options related to the XML output +#--------------------------------------------------------------------------- +GENERATE_XML = NO +XML_OUTPUT = xml +XML_PROGRAMLISTING = YES +#--------------------------------------------------------------------------- +# Configuration options related to the DOCBOOK output +#--------------------------------------------------------------------------- +GENERATE_DOCBOOK = NO +DOCBOOK_OUTPUT = docbook +DOCBOOK_PROGRAMLISTING = NO +#--------------------------------------------------------------------------- +# Configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- +GENERATE_AUTOGEN_DEF = NO +#--------------------------------------------------------------------------- +# Configuration options related to the Perl module output +#--------------------------------------------------------------------------- +GENERATE_PERLMOD = NO +PERLMOD_LATEX = NO +PERLMOD_PRETTY = YES +PERLMOD_MAKEVAR_PREFIX = +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = YES +EXPAND_ONLY_PREDEF = YES +SEARCH_INCLUDES = YES +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = +PREDEFINED = NS_UNAVAILABLE \ + "NS_ENUM(type, name)=enum name" \ + NS_ASSUME_NONNULL_BEGIN \ + NS_ASSUME_NONNULL_END +EXPAND_AS_DEFINED = +SKIP_FUNCTION_MACROS = YES +#--------------------------------------------------------------------------- +# Configuration options related to external references +#--------------------------------------------------------------------------- +TAGFILES = +GENERATE_TAGFILE = +ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES +EXTERNAL_PAGES = YES +PERL_PATH = /usr/bin/perl +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- +CLASS_DIAGRAMS = YES +MSCGEN_PATH = +DIA_PATH = +HIDE_UNDOC_RELATIONS = YES +HAVE_DOT = NO +DOT_NUM_THREADS = 0 +DOT_FONTNAME = Helvetica +DOT_FONTSIZE = 10 +DOT_FONTPATH = +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +GROUP_GRAPHS = YES +UML_LOOK = NO +UML_LIMIT_NUM_FIELDS = 10 +TEMPLATE_RELATIONS = NO +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +CALL_GRAPH = NO +CALLER_GRAPH = NO +GRAPHICAL_HIERARCHY = YES +DIRECTORY_GRAPH = YES +DOT_IMAGE_FORMAT = png +INTERACTIVE_SVG = NO +DOT_PATH = +DOTFILE_DIRS = +MSCFILE_DIRS = +DIAFILE_DIRS = +PLANTUML_JAR_PATH = +PLANTUML_INCLUDE_PATH = +DOT_GRAPH_MAX_NODES = 50 +MAX_DOT_GRAPH_DEPTH = 0 +DOT_TRANSPARENT = NO +DOT_MULTI_TARGETS = NO +GENERATE_LEGEND = YES +DOT_CLEANUP = YES + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/Package.swift b/Package.swift new file mode 100644 index 0000000..563bdfd --- /dev/null +++ b/Package.swift @@ -0,0 +1,98 @@ +// swift-tools-version:5.1 +// The swift-tools-version declares the minimum version of Swift required to build this package. +import PackageDescription + +/*! @file Package.swift + @brief AppAuth iOS SDK + @copyright + Copyright 2020 The AppAuth Authors. All Rights Reserved. + @copydetails + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +let package = Package( + name: "AppAuth", + platforms: [ + .macOS(.v10_10), + .iOS(.v8), + .tvOS(.v9), + .watchOS(.v2) + ], + products: [ + .library( + name: "AppAuthCore", + targets: ["AppAuthCore"]), + .library( + name: "AppAuth", + targets: ["AppAuth"]), + .library( + name: "AppAuthEnterpriseUserAgent", + targets: ["AppAuthEnterpriseUserAgent"]), + .library( + name: "AppAuthTV", + targets: ["AppAuthTV"]) + ], + dependencies: [], + targets: [ + .target( + name: "AppAuthCore", + path: "Source/AppAuthCore", + publicHeadersPath: "" + ), + .target( + name: "AppAuth", + dependencies: ["AppAuthCore"], + path: "Source/AppAuth", + sources: ["iOS", "macOS"], + publicHeadersPath: "", + cSettings: [ + .headerSearchPath("iOS"), + .headerSearchPath("macOS"), + .headerSearchPath("macOS/LoopbackHTTPServer"), + ] + ), + .target( + name: "AppAuthEnterpriseUserAgent", + dependencies: ["AppAuthCore"], + path: "Source/AppAuthEnterpriseUserAgent", + sources: ["iOS"], + publicHeadersPath: "", + cSettings: [ + .headerSearchPath("iOS"), + ] + ), + .target( + name: "AppAuthTV", + dependencies: ["AppAuthCore"], + path: "Source/AppAuthTV", + publicHeadersPath: "" + ), + .testTarget( + name: "AppAuthCoreTests", + dependencies: ["AppAuthCore"], + path: "UnitTests", + exclude: ["OIDSwiftTests.swift", "AppAuthTV"] + ), + .testTarget( + name: "AppAuthCoreSwiftTests", + dependencies: ["AppAuthCore"], + path: "UnitTests", + sources: ["OIDSwiftTests.swift"] + ), + .testTarget( + name: "AppAuthTVTests", + dependencies: ["AppAuthTV"], + path: "UnitTests/AppAuthTV" + ), + ] +) diff --git a/README.md b/README.md index 2cb1859..0512234 100644 --- a/README.md +++ b/README.md @@ -1,92 +1,738 @@ -# DropboxSimpleOAuth [![Bitrise](https://app.bitrise.io/app/aee74397c8b444ee/status.svg?token=CHhwqT13JzZqLOpVyGQjRw&branch=master)](https://app.bitrise.io/app/aee74397c8b444ee) [![Cocoapod Version](https://img.shields.io/cocoapods/v/DropboxSimpleOAuth.svg)](http://cocoapods.org/?q=DropboxSimpleOAuth) [![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) [![Cocoapod Platform](http://img.shields.io/badge/platform-iOS-blue.svg)](http://cocoapods.org/?q=DropboxSimpleOAuth) [![License](https://img.shields.io/dub/l/vibe-d.svg)](https://github.com/rbaumbach/DropboxSimpleOAuth/blob/master/MIT-LICENSE.txt) +![AppAuth for iOS and macOS](https://rawgit.com/openid/AppAuth-iOS/master/appauth_lockup.svg) +[![Build Status](https://travis-ci.org/openid/AppAuth-iOS.svg?branch=master)](https://travis-ci.org/openid/AppAuth-iOS) +[![codecov](https://codecov.io/gh/openid/AppAuth-iOS/branch/master/graph/badge.svg)](https://codecov.io/gh/openid/AppAuth-iOS) +[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-brightgreen.svg?style=flat)](https://github.com/Carthage/Carthage) +[![SwiftPM compatible](https://img.shields.io/badge/SwiftPM-compatible-brightgreen.svg?style=flat)](https://swift.org/package-manager) +[![Pod Version](https://img.shields.io/cocoapods/v/AppAuth.svg?style=flat)](https://cocoapods.org/pods/AppAuth) +[![Pod License](https://img.shields.io/cocoapods/l/AppAuth.svg?style=flat)](https://github.com/openid/AppAuth-iOS/blob/master/LICENSE) +[![Pod Platform](https://img.shields.io/cocoapods/p/AppAuth.svg?style=flat)](https://cocoapods.org/pods/AppAuth) +[![Catalyst compatible](https://img.shields.io/badge/Catalyst-compatible-brightgreen.svg?style=flat)](https://developer.apple.com/documentation/xcode/creating_a_mac_version_of_your_ipad_app) -A quick and simple way to authenticate a Dropbox user in your iPhone or iPad app. +AppAuth for iOS and macOS, and tvOS is a client SDK for communicating with +[OAuth 2.0](https://tools.ietf.org/html/rfc6749) and +[OpenID Connect](http://openid.net/specs/openid-connect-core-1_0.html) providers. +It strives to +directly map the requests and responses of those specifications, while following +the idiomatic style of the implementation language. In addition to mapping the +raw protocol flows, convenience methods are available to assist with common +tasks like performing an action with fresh tokens. -

- iPhone 7 Screenshot - iPad Pro 9.7 Screenshot -

+It follows the best practices set out in +[RFC 8252 - OAuth 2.0 for Native Apps](https://tools.ietf.org/html/rfc8252) +including using `SFAuthenticationSession` and `SFSafariViewController` on iOS +for the auth request. `UIWebView` and `WKWebView` are explicitly *not* +supported due to the security and usability reasons explained in +[Section 8.12 of RFC 8252](https://tools.ietf.org/html/rfc8252#section-8.12). -## Adding DropboxSimpleOAuth to your project +It also supports the [PKCE](https://tools.ietf.org/html/rfc7636) extension to +OAuth, which was created to secure authorization codes in public clients when +custom URI scheme redirects are used. The library is friendly to other +extensions (standard or otherwise), with the ability to handle additional params +in all protocol requests and responses. + +For tvOS, AppAuth implements [OAuth 2.0 Device Authorization Grant +](https://tools.ietf.org/html/rfc8628) to allow for tvOS sign-ins through a secondary device. + +## Specification + +### iOS + +#### Supported Versions + +AppAuth supports iOS 7 and above. + +iOS 9+ uses the in-app browser tab pattern +(via `SFSafariViewController`), and falls back to the system browser (mobile +Safari) on earlier versions. + +#### Authorization Server Requirements + +Both Custom URI Schemes (all supported versions of iOS) and Universal Links +(iOS 9+) can be used with the library. + +In general, AppAuth can work with any authorization server that supports +native apps, as documented in [RFC 8252](https://tools.ietf.org/html/rfc8252), +either through custom URI scheme redirects, or universal links. +Authorization servers that assume all clients are web-based, or require clients to maintain +confidentiality of the client secrets may not work well. + +### macOS + +#### Supported Versions + +AppAuth supports macOS (OS X) 10.9 and above. + +#### Authorization Server Requirements + +AppAuth for macOS supports both custom schemes; a loopback HTTP redirects +via a small embedded server. + +In general, AppAuth can work with any authorization server that supports +native apps, as documented in [RFC 8252](https://tools.ietf.org/html/rfc8252); +either through custom URI schemes, or loopback HTTP redirects. +Authorization servers that assume all clients are web-based, or require clients to maintain +confidentiality of the client secrets may not work well. + +### tvOS + +#### Supported Versions + +AppAuth supports tvOS 9.0 and above. Please note that while it is possible to run the standard AppAuth library on tvOS, the documentation below describes implementing [OAuth 2.0 Device Authorization Grant](https://tools.ietf.org/html/rfc8628) (AppAuthTV). + +#### Authorization Server Requirements + +AppAuthTV is designed for servers that support the device authorization flow as documented in [RFC 8628](https://tools.ietf.org/html/rfc8628). + +## Try + +Want to try out AppAuth? Just run: + + pod try AppAuth + +Follow the instructions in [Examples/README.md](Examples/README.md) to configure +with your own OAuth client (you need to update three configuration points with your +client info to try the demo). + +## Setup + +AppAuth supports four options for dependency management. ### CocoaPods -[CocoaPods](http://cocoapods.org) is the recommended way to add DropboxSimpleOAuth to your project. +With [CocoaPods](https://guides.cocoapods.org/using/getting-started.html), +add the following line to your `Podfile`: + + pod 'AppAuth' + +Then, run `pod install`. + +**tvOS:** Use the `TV` subspec: + + pod 'AppAuth/TV' -1. Add DropboxSimpleOAuth to your Podfile `pod 'DropboxSimpleOAuth'`. -2. Install the pod(s) by running `pod install`. -3. Add DropboxSimpleOAuth to your files with `#import `. + +### Swift Package Manager + +With [Swift Package Manager](https://swift.org/package-manager), +add the following `dependency` to your `Package.swift`: + +```swift +dependencies: [ + .package(url: "https://github.com/openid/AppAuth-iOS.git", .upToNextMajor(from: "1.3.0")) +] +``` + +**tvOS:** Use the `AppAuthTV` target. ### Carthage -1. Add `github "rbaumbach/DropboxSimpleOAuth"` to your Cartfile. -2. [Follow the directions](https://github.com/Carthage/Carthage#getting-started) to add the dynamic framework to your target. +With [Carthage](https://github.com/Carthage/Carthage), add the following +line to your `Cartfile`: + + github "openid/AppAuth-iOS" "master" + +Then, run `carthage bootstrap`. + +**tvOS:** Use the `AppAuthTV` framework. + +### Static Library -### Clone from Github +You can also use AppAuth as a static library. This requires linking the library +and your project, and including the headers. Here is a suggested configuration: -1. Clone repository from github and copy files directly, or add it as a git submodule. -2. Add all files from 'Source' directory to your project. +1. Create an Xcode Workspace. +2. Add `AppAuth.xcodeproj` to your Workspace. +3. Include libAppAuth as a linked library for your target (in the "General -> +Linked Framework and Libraries" section of your target). +4. Add `AppAuth-iOS/Source` to your search paths of your target ("Build Settings -> +"Header Search Paths"). -## How To +*Note: There is no static library for AppAuthTV.* -* Create an instance of `DropboxSimpleOAuthViewController` and pass in an [Dropbox app key, app secret, client callback URL](https://www.dropbox.com/developers) and completion block to be executed with `DropboxLoginResponse` and `NSError` arguments. -* Once the instance of `DropboxSimpleOAuthViewController` is presented (either as a modal or pushed on the navigation stack), it will allow the user to login. After the user logs in, the completion block given in the initialization of the view controller will be executed. The argument in the completion block, `DropboxLoginResponse`, contains an accessToken and other login information for the authenticated user provided by [Dropbox API Response](https://www.dropbox.com/developers/core/docs#oa2-token). If there is an issue attempting to authenticate, an error will be given instead. -* By default, if there are issues with authentication, an UIAlertView will be given to the user. To disable this, and rely on the NSError directly, set the property `shouldShowErrorAlert` to NO. -* Note: Even though an instance of the view controller itself can be initalized without app key, app secret, client callback and completion block (to help with testing), this data must be set using the view controller's properties before it is presented to the user. +## Auth Flow -### Example Usage +AppAuth supports both manual interaction with the authorization server +where you need to perform your own token exchanges, as well as convenience +methods that perform some of this logic for you. This example uses the +convenience method, which returns either an `OIDAuthState` object, or an error. -```objective-c -// Simplest Example: +`OIDAuthState` is a class that keeps track of the authorization and token +requests and responses, and provides a convenience method to call an API with +fresh tokens. This is the only object that you need to serialize to retain the +authorization state of the session. -DropboxSimpleOAuthViewController - *viewController = [[DropboxSimpleOAuthViewController alloc] initWithAppKey:@"123I_am_a_client_id_567890" - appSecret:@"shhhhhh, I'm a secret" - callbackURL:[NSURL URLWithString:@"http://your.fancy.site"] - completion:^(DropboxLoginResponse *response, NSError *error) { - NSLog(@"My Access Token is: %@", response.accessToken); - }]; -[self.navigationController pushViewController:viewController - animated:YES]; +### Configuration -// Disable error UIAlertViews Example: +You can configure AppAuth by specifying the endpoints directly: -DropboxSimpleOAuthViewController - *viewController = [[DropboxSimpleOAuthViewController alloc] initWithAppKey:@"123I_am_a_client_id_567890" - appSecret:@"shhhhhh, I'm a secret" - callbackURL:[NSURL URLWithString:@"http://your.fancy.site"] - completion:^(DropboxLoginResponse *response, NSError *error) { - NSLog(@"My OAuth Token is: %@", response.accessToken); - }]; -viewController.shouldShowErrorAlert = NO; +Objective-C +```objc +NSURL *authorizationEndpoint = + [NSURL URLWithString:@"https://accounts.google.com/o/oauth2/v2/auth"]; +NSURL *tokenEndpoint = + [NSURL URLWithString:@"https://www.googleapis.com/oauth2/v4/token"]; -[self.navigationController pushViewController:viewController - animated:YES]; +OIDServiceConfiguration *configuration = + [[OIDServiceConfiguration alloc] + initWithAuthorizationEndpoint:authorizationEndpoint + tokenEndpoint:tokenEndpoint]; + +// perform the auth request... +``` + +Swift +```swift +let authorizationEndpoint = URL(string: "https://accounts.google.com/o/oauth2/v2/auth")! +let tokenEndpoint = URL(string: "https://www.googleapis.com/oauth2/v4/token")! +let configuration = OIDServiceConfiguration(authorizationEndpoint: authorizationEndpoint, + tokenEndpoint: tokenEndpoint) + +// perform the auth request... +``` + +**tvOS** + +Objective-C +```objc +NSURL *deviceAuthorizationEndpoint = + [NSURL URLWithString:@"https://oauth2.googleapis.com/device/code"]; +NSURL *tokenEndpoint = + [NSURL URLWithString:@"https://www.googleapis.com/oauth2/v4/token"]; + +OIDTVServiceConfiguration *configuration = + [[OIDTVServiceConfiguration alloc] + initWithDeviceAuthorizationEndpoint:deviceAuthorizationEndpoint + tokenEndpoint:tokenEndpoint]; + +// perform the auth request... ``` -## Testing -* Prerequisites: [ruby](https://github.com/sstephenson/rbenv), [ruby gems](https://rubygems.org/pages/download), [bundler](http://bundler.io) +Or through discovery: + +Objective-C +```objc +NSURL *issuer = [NSURL URLWithString:@"https://accounts.google.com"]; -This project has been setup to use [fastlane](https://fastlane.tools) to run the specs. +[OIDAuthorizationService discoverServiceConfigurationForIssuer:issuer + completion:^(OIDServiceConfiguration *_Nullable configuration, + NSError *_Nullable error) { -First, run the setup.sh script to bundle required gems and CocoaPods when in the project directory: + if (!configuration) { + NSLog(@"Error retrieving discovery document: %@", + [error localizedDescription]); + return; + } -```bash -$ ./setup.sh + // perform the auth request... +}]; ``` -And then use fastlane to run all the specs on the command line: +Swift +```swift +let issuer = URL(string: "https://accounts.google.com")! -```bash -$ bundle exec fastlane specs +// discovers endpoints +OIDAuthorizationService.discoverConfiguration(forIssuer: issuer) { configuration, error in + guard let config = configuration else { + print("Error retrieving discovery document: \(error?.localizedDescription ?? "Unknown error")") + return + } + + // perform the auth request... +} +``` + +**tvOS** + +Objective-C +```objc +NSURL *issuer = [NSURL URLWithString:@"https://accounts.google.com"]; + +[OIDTVAuthorizationService discoverServiceConfigurationForIssuer:issuer + completion:^(OIDTVServiceConfiguration *_Nullable configuration, + NSError *_Nullable error) { + + if (!configuration) { + NSLog(@"Error retrieving discovery document: %@", + [error localizedDescription]); + return; + } + + // perform the auth request... +}]; ``` -## Version History +### Authorizing – iOS + +First, you need to have a property in your `UIApplicationDelegate` +implementation to hold the session, in order to continue the authorization flow +from the redirect. In this example, the implementation of this delegate is +a class named `AppDelegate`, if your app's application delegate has a different +name, please update the class name in samples below accordingly. + +Objective-C +```objc +@interface AppDelegate : UIResponder +// property of the app's AppDelegate +@property(nonatomic, strong, nullable) id currentAuthorizationFlow; +@end +``` + +Swift +```swift +class AppDelegate: UIResponder, UIApplicationDelegate { + // property of the app's AppDelegate + var currentAuthorizationFlow: OIDExternalUserAgentSession? +} +``` + + +And your main class, a property to store the auth state: + +Objective-C +```objc +// property of the containing class +@property(nonatomic, strong, nullable) OIDAuthState *authState; +``` +Swift +```swift +// property of the containing class +private var authState: OIDAuthState? +``` + + +Then, initiate the authorization request. By using the +`authStateByPresentingAuthorizationRequest` convenience method, the token +exchange will be performed automatically, and everything will be protected with +PKCE (if the server supports it). AppAuth also lets you perform these +requests manually. See the `authNoCodeExchange` method in the included Example +app for a demonstration: + +Objective-C +```objc +// builds authentication request +OIDAuthorizationRequest *request = + [[OIDAuthorizationRequest alloc] initWithConfiguration:configuration + clientId:kClientID + scopes:@[OIDScopeOpenID, + OIDScopeProfile] + redirectURL:kRedirectURI + responseType:OIDResponseTypeCode + additionalParameters:nil]; + +// performs authentication request +AppDelegate *appDelegate = + (AppDelegate *)[UIApplication sharedApplication].delegate; +appDelegate.currentAuthorizationFlow = + [OIDAuthState authStateByPresentingAuthorizationRequest:request + presentingViewController:self + callback:^(OIDAuthState *_Nullable authState, + NSError *_Nullable error) { + if (authState) { + NSLog(@"Got authorization tokens. Access token: %@", + authState.lastTokenResponse.accessToken); + [self setAuthState:authState]; + } else { + NSLog(@"Authorization error: %@", [error localizedDescription]); + [self setAuthState:nil]; + } +}]; +``` + +Swift +```swift +// builds authentication request +let request = OIDAuthorizationRequest(configuration: configuration, + clientId: clientID, + clientSecret: clientSecret, + scopes: [OIDScopeOpenID, OIDScopeProfile], + redirectURL: redirectURI, + responseType: OIDResponseTypeCode, + additionalParameters: nil) + +// performs authentication request +print("Initiating authorization request with scope: \(request.scope ?? "nil")") + +let appDelegate = UIApplication.shared.delegate as! AppDelegate + +appDelegate.currentAuthorizationFlow = + OIDAuthState.authState(byPresenting: request, presenting: self) { authState, error in + if let authState = authState { + self.setAuthState(authState) + print("Got authorization tokens. Access token: " + + "\(authState.lastTokenResponse?.accessToken ?? "nil")") + } else { + print("Authorization error: \(error?.localizedDescription ?? "Unknown error")") + self.setAuthState(nil) + } +} +``` + +*Handling the Redirect* + +The authorization response URL is returned to the app via the iOS openURL +app delegate method, so you need to pipe this through to the current +authorization session (created in the previous session): + +Objective-C +```objc +- (BOOL)application:(UIApplication *)app + openURL:(NSURL *)url + options:(NSDictionary *)options { + // Sends the URL to the current authorization flow (if any) which will + // process it if it relates to an authorization response. + if ([_currentAuthorizationFlow resumeExternalUserAgentFlowWithURL:url]) { + _currentAuthorizationFlow = nil; + return YES; + } + + // Your additional URL handling (if any) goes here. + + return NO; +} +``` + +Swift +```swift +func application(_ app: UIApplication, + open url: URL, + options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool { + // Sends the URL to the current authorization flow (if any) which will + // process it if it relates to an authorization response. + if let authorizationFlow = self.currentAuthorizationFlow, + authorizationFlow.resumeExternalUserAgentFlow(with: url) { + self.currentAuthorizationFlow = nil + return true + } + + // Your additional URL handling (if any) + + return false +} +``` + +### Authorizing – MacOS + +On macOS, the most popular way to get the authorization response redirect is to +start a local HTTP server on the loopback interface (limited to incoming +requests from the user's machine only). When the authorization is complete, the +user is redirected to that local server, and the authorization response can be +processed by the app. AppAuth takes care of managing the local HTTP server +lifecycle for you. + +> #### :bulb: Alternative: Custom URI Schemes +> Custom URI schemes are also supported on macOS, but some browsers display +> an interstitial, which reduces the usability. For an example on using custom +> URI schemes with macOS, See `Example-Mac`. + +To receive the authorization response using a local HTTP server, first you need +to have an instance variable in your main class to retain the HTTP redirect +handler: + +Objective-C +```objc +OIDRedirectHTTPHandler *_redirectHTTPHandler; +``` + +Then, as the port used by the local HTTP server varies, you need to start it +before building the authorization request, in order to get the exact redirect +URI to use: + +Objective-C +```objc +static NSString *const kSuccessURLString = + @"http://openid.github.io/AppAuth-iOS/redirect/"; +NSURL *successURL = [NSURL URLWithString:kSuccessURLString]; + +// Starts a loopback HTTP redirect listener to receive the code. This needs to be started first, +// as the exact redirect URI (including port) must be passed in the authorization request. +_redirectHTTPHandler = [[OIDRedirectHTTPHandler alloc] initWithSuccessURL:successURL]; +NSURL *redirectURI = [_redirectHTTPHandler startHTTPListener:nil]; +``` + +Then, initiate the authorization request. By using the +`authStateByPresentingAuthorizationRequest` convenience method, the token +exchange will be performed automatically, and everything will be protected with +PKCE (if the server supports it). By assigning the return value to the +`OIDRedirectHTTPHandler`'s `currentAuthorizationFlow`, the authorization will +continue automatically once the user makes their choice: + +```objc +// builds authentication request +OIDAuthorizationRequest *request = + [[OIDAuthorizationRequest alloc] initWithConfiguration:configuration + clientId:kClientID + clientSecret:kClientSecret + scopes:@[ OIDScopeOpenID ] + redirectURL:redirectURI + responseType:OIDResponseTypeCode + additionalParameters:nil]; +// performs authentication request +__weak __typeof(self) weakSelf = self; +_redirectHTTPHandler.currentAuthorizationFlow = + [OIDAuthState authStateByPresentingAuthorizationRequest:request + callback:^(OIDAuthState *_Nullable authState, + NSError *_Nullable error) { + // Brings this app to the foreground. + [[NSRunningApplication currentApplication] + activateWithOptions:(NSApplicationActivateAllWindows | + NSApplicationActivateIgnoringOtherApps)]; + + // Processes the authorization response. + if (authState) { + NSLog(@"Got authorization tokens. Access token: %@", + authState.lastTokenResponse.accessToken); + } else { + NSLog(@"Authorization error: %@", error.localizedDescription); + } + [weakSelf setAuthState:authState]; +}]; +``` + + +### Authorizing – tvOS + +Ensure that your main class is a delegate of `OIDAuthStateChangeDelegate`, `OIDAuthStateErrorDelegate`, implement the corresponding methods, and include the following property and instance variable: + +Objective-C +```objc +// property of the containing class +@property(nonatomic, strong, nullable) OIDAuthState *authState; + +// instance variable of the containing class +OIDTVAuthorizationCancelBlock _cancelBlock; +``` + +Then, build and perform the authorization request. + +Objective-C +```objc +// builds authentication request +__weak __typeof(self) weakSelf = self; + +OIDTVAuthorizationRequest *request = + [[OIDTVAuthorizationRequest alloc] initWithConfiguration:configuration + clientId:kClientID + clientSecret:kClientSecret + scopes:@[ OIDScopeOpenID, OIDScopeProfile ] + additionalParameters:nil]; + +// performs authentication request +OIDTVAuthorizationInitialization initBlock = + ^(OIDTVAuthorizationResponse *_Nullable response, NSError *_Nullable error) { + if (response) { + // process authorization response + NSLog(@"Got authorization response: %@", response); + } else { + // handle initialization error + NSLog(@"Error: %@", error); + } + }; + +OIDTVAuthorizationCompletion completionBlock = + ^(OIDAuthState *_Nullable authState, NSError *_Nullable error) { + weakSelf.signInView.hidden = YES; + if (authState) { + NSLog(@"Token response: %@", authState.lastTokenResponse); + [weakSelf setAuthState:authState]; + } else { + NSLog(@"Error: %@", error); + [weakSelf setAuthState:nil]; + } + }; + +_cancelBlock = [OIDTVAuthorizationService authorizeTVRequest:request + initialization:initBlock + completion:completionBlock]; +``` + +### Making API Calls + +AppAuth gives you the raw token information, if you need it. However, we +recommend that users of the `OIDAuthState` convenience wrapper use the provided +`performActionWithFreshTokens:` method to perform their API calls to avoid +needing to worry about token freshness: + +Objective-C +```objc +[_authState performActionWithFreshTokens:^(NSString *_Nonnull accessToken, + NSString *_Nonnull idToken, + NSError *_Nullable error) { + if (error) { + NSLog(@"Error fetching fresh tokens: %@", [error localizedDescription]); + return; + } + + // perform your API request using the tokens +}]; +``` + +Swift +```swift +let userinfoEndpoint = URL(string:"https://openidconnect.googleapis.com/v1/userinfo")! +self.authState?.performAction() { (accessToken, idToken, error) in + + if error != nil { + print("Error fetching fresh tokens: \(error?.localizedDescription ?? "Unknown error")") + return + } + guard let accessToken = accessToken else { + return + } + + // Add Bearer token to request + var urlRequest = URLRequest(url: userinfoEndpoint) + urlRequest.allHTTPHeaderFields = ["Authorization": "Bearer \(accessToken)"] + + // Perform request... +} +``` + +### Custom User-Agents (iOS and macOS) + +Each OAuth flow involves presenting an external user-agent to the user, that +allows them to interact with the OAuth authorization server. Typical examples +of a user-agent are the user's browser, or an in-app browser tab incarnation +like `ASWebAuthenticationSession` on iOS. + +AppAuth ships with several implementations of an external user-agent out of the +box, including defaults for iOS and macOS suitable for most cases. The default +user-agents typically share persistent cookies with the system default browser, +to improve the chance that the user doesn't need to sign-in all over again. + +It is possible to change the user-agent that AppAuth uses, and even write your +own - all without needing to fork the library. + +All implementations of the external user-agent, be they included or created by +you need to conform to the +[`OIDExternalUserAgent`](http://openid.github.io/AppAuth-iOS/docs/latest/protocol_o_i_d_external_user_agent-p.html) +protocol. + +Instances of the `OIDExternalUserAgent`are passed into +[`OIDAuthState.authStateByPresentingAuthorizationRequest:externalUserAgent:callback`](http://openid.github.io/AppAuth-iOS/docs/latest/interface_o_i_d_auth_state.html#ac762fe2bf95c116f0b437419be211fa1) +and/or +[`OIDAuthorizationService.presentAuthorizationRequest:externalUserAgent:callback:`](http://openid.github.io/AppAuth-iOS/docs/latest/interface_o_i_d_authorization_service.html#ae551f8e6887366a46e49b09b37389b8f) +rather than using the platform-specific convenience methods (which use the +default user-agents for their respective platforms), like +[`OIDAuthState.authStateByPresentingAuthorizationRequest:presentingViewController:callback:`](http://openid.github.io/AppAuth-iOS/docs/latest/category_o_i_d_auth_state_07_i_o_s_08.html#ae32fd0732cd3192cd5219f2655a4c85c). + +Popular use-cases for writing your own user-agent implementation include needing +to style the user-agent in ways not supported by AppAuth, and implementing a +fully custom flow with your own business logic. You can take one of the existing +implementations as a starting point to copy, rename, and customize to your +needs. + +#### Custom Browser User-Agent + +AppAuth for iOS includes a few extra user-agent implementations which you can +try, or use as a reference for your own implementation. One of them, +[`OIDExternalUserAgentIOSCustomBrowser`](http://openid.github.io/AppAuth-iOS/docs/latest/interface_o_i_d_external_user_agent_i_o_s_custom_browser.html) +enables you to use a different browser for authentication, like Chrome for iOS +or Firefox for iOS. + +##### CocoaPods +Include the `EnterpriseUserAgent` subspec alongside any pods/subspecs you were already using, e.g.: +``` + pod 'AppAuth' + pod 'AppAuth/EnterpriseUserAgent' +``` + +Make sure to import `AppAuthEnterpriseUserAgent.h` in addition to `AppAuth.h` if you are using the full `AppAuth` functionality. + +##### Carthage +Use the `AppAuthEnterpriseUserAgent` framework, which includes all the headers of the `AppAuth` framework. +Make sure to import ``. This includes all the files included by AppAuth/AppAuthCore, so only this import is necessary. + +##### Swift Package Manager +Include the `AppAuthEnterpriseUserAgent` target alongside any targets you were already using. + +Make sure to import `AppAuthEnterpriseUserAgent.h` in addition to `AppAuth.h` if you are using the full `AppAuth` functionality. + +Here's how to configure AppAuth to use a custom browser using the +`OIDExternalUserAgentIOSCustomBrowser` user agent: + +First, add the following array to your +[Info.plist](https://github.com/openid/AppAuth-iOS/blob/135f99d2cb4e9d18d310ac2588b905e612461561/Examples/Example-iOS_ObjC/Source/Info.plist#L34) +(in XCode, right click -> Open As -> Source Code) + +``` + LSApplicationQueriesSchemes + + googlechromes + opera-https + firefox + +``` + +This is required so that AppAuth can test for the browser and open the app store +if it's not installed (the default behavior of this user-agent). You only need +to include the URL scheme of the actual browser you intend to use. + +Next, make sure to import the correct header file. +If using CocoaPods/Swift Package manager, make sure to import AppAuthEnterpriseUserAgent alongside AppAuth/AppAuthCore. + +Objective-C +```objc +// performs authentication request +AppDelegate *appDelegate = + (AppDelegate *)[UIApplication sharedApplication].delegate; +id userAgent = + [OIDExternalUserAgentIOSCustomBrowser CustomBrowserChrome]; +appDelegate.currentAuthorizationFlow = + [OIDAuthState authStateByPresentingAuthorizationRequest:request + externalUserAgent:userAgent + callback:^(OIDAuthState *_Nullable authState, + NSError *_Nullable error) { + if (authState) { + NSLog(@"Got authorization tokens. Access token: %@", + authState.lastTokenResponse.accessToken); + [self setAuthState:authState]; + } else { + NSLog(@"Authorization error: %@", [error localizedDescription]); + [self setAuthState:nil]; + } +}]; +``` + +Swift +``` +guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { + self.logMessage("Error accessing AppDelegate") + return + } +let userAgent = OIDExternalUserAgentIOSCustomBrowser.customBrowserChrome() +appDelegate.currentAuthorizationFlow = OIDAuthState.authState(byPresenting: request, externalUserAgent: userAgent) { authState, error in + if let authState = authState { + self.setAuthState(authState) + self.logMessage("Got authorization tokens. Access token: \(authState.lastTokenResponse?.accessToken ?? "DEFAULT_TOKEN")") + } else { + self.logMessage("Authorization error: \(error?.localizedDescription ?? "DEFAULT_ERROR")") + self.setAuthState(nil) + } +} +``` + +That's it! With those two changes (which you can try on the included sample), +AppAuth will use Chrome iOS for the authorization request (and open Chrome in +the App Store if it's not installed). + +⚠️**Note: the `OIDExternalUserAgentIOSCustomBrowser` user-agent is not intended for consumer apps**. It is designed for +advanced enterprise use-cases where the app developers have greater control over +the operating environment and have special requirements that require a custom +browser like Chrome. + +You don't need to stop with the included external user agents either! Since the +[`OIDExternalUserAgent`](http://openid.github.io/AppAuth-iOS/docs/latest/protocol_o_i_d_external_user_agent-p.html) +protocol is part of AppAuth's public API, you can implement your own versions of +it. In the above example, +`userAgent = [OIDExternalUserAgentIOSCustomBrowser CustomBrowserChrome]` would +be replaced with an instantiation of your user-agent implementation. + +## API Documentation -Version history can be found [on releases page](https://github.com/rbaumbach/DropboxSimpleOAuth/releases). +Browse the [API documentation](http://openid.github.io/AppAuth-iOS/docs/latest/annotated.html). -## Suggestions, requests, and feedback +## Included Samples -Thanks for checking out DropboxSimpleOAuth for your in-app Dropbox authentication. Any feedback can be can be sent to: github@ryan.codes. +Sample apps that explore core AppAuth features are available for iOS, macOS and tvOS; follow the instructions in [Examples/README.md](Examples/README.md) to get started. diff --git a/appauth_lockup.svg b/appauth_lockup.svg new file mode 100644 index 0000000..5cbcc13 --- /dev/null +++ b/appauth_lockup.svg @@ -0,0 +1 @@ + \ No newline at end of file