diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml deleted file mode 100644 index 2b1173c..0000000 --- a/.github/workflows/codeql.yml +++ /dev/null @@ -1,81 +0,0 @@ -# This workflow will run CodeQL analysis. -name: CodeQL - -on: - push: - branches: [ "main" ] - pull_request: - branches: [ "main" ] - types: [opened, synchronize, reopened, ready_for_review] - schedule: - - cron: '30 7 * * 3' - -jobs: - - analyze: - - # Only run this action if the PR isn't a draft and it is labled as a `security` PR - if: github.event.pull_request.draft == false && contains(github.event.pull_request.labels.*.name, 'security') - - name: Analyze (${{ matrix.language }}) - # Runner size impacts CodeQL analysis time. To learn more, please see: - # - https://gh.io/recommended-hardware-resources-for-running-codeql - # - https://gh.io/supported-runners-and-hardware-resources - # - https://gh.io/using-larger-runners (GitHub.com only) - # Consider using larger runners or machines with greater resources for possible analysis time improvements. - - runs-on: macos-26 - timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }} - permissions: - # required for all workflows - security-events: write - - # required to fetch internal or private CodeQL packs - packages: read - - # only required for workflows in private repositories - actions: read - contents: read - - strategy: - fail-fast: false - matrix: - include: - - language: swift - build-mode: autobuild - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - name: Initialize CodeQL - uses: github/codeql-action/init@v3 - with: - languages: ${{ matrix.language }} - build-mode: ${{ matrix.build-mode }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - - # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs - # queries: security-extended,security-and-quality - - # If the analyze step fails for one of the languages you are analyzing with - # "We were unable to automatically build your code", modify the matrix above - # to set the build mode to "manual" for that language. Then modify this step - # to build your code. - # â„šī¸ Command-line programs to run using the OS shell. - # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun - - if: matrix.build-mode == 'manual' - shell: bash - run: | - echo 'If you are using a "manual" build mode for one or more of the' \ - 'languages you are analyzing, replace this with the commands to build' \ - 'your code, for example:' - echo ' make bootstrap' - echo ' make release' - exit 1 - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 - with: - category: "/language:${{matrix.language}}" diff --git a/Package.swift b/Package.swift index fc4f96c..ab74f96 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version: 6.2 +// swift-tools-version: 6.1 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription @@ -6,11 +6,11 @@ import PackageDescription let package = Package( name: "OAuthKit", platforms: [ - .iOS(.v26), - .macOS(.v26), - .tvOS(.v26), - .visionOS(.v26), - .watchOS(.v26) + .iOS(.v17), + .macOS(.v15), + .tvOS(.v18), + .visionOS(.v1), + .watchOS(.v10) ], products: [ .library( diff --git a/README.md b/README.md index 3ee7745..943d007 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ ![Build](https://github.com/codefiesta/OAuthKit/actions/workflows/swift.yml/badge.svg) -![Swift 6.2+](https://img.shields.io/badge/Swift-6.2%2B-gold.svg) +![Swift 6.1+](https://img.shields.io/badge/Swift-6.1%2B-gold.svg) ![Xcode 26.0+](https://img.shields.io/badge/Xcode-26.0%2B-tomato.svg) -![iOS 26.0+](https://img.shields.io/badge/iOS-26.0%2B-crimson.svg) -![macOS 26.0+](https://img.shields.io/badge/macOS-26.0%2B-skyblue.svg) -![tvOS 26.0+](https://img.shields.io/badge/tvOS-26.0%2B-blue.svg) -![visionOS 26.0+](https://img.shields.io/badge/visionOS-26.0%2B-violet.svg) -![watchOS 26.0+](https://img.shields.io/badge/watchOS-26.0%2B-magenta.svg) +![iOS 17.0+](https://img.shields.io/badge/iOS-17.0%2B-crimson.svg) +![macOS 15.0+](https://img.shields.io/badge/macOS-15.0%2B-skyblue.svg) +![tvOS 18.0+](https://img.shields.io/badge/tvOS-18.0%2B-blue.svg) +![visionOS 1.0+](https://img.shields.io/badge/visionOS-1.0%2B-violet.svg) +![watchOS 10.0+](https://img.shields.io/badge/watchOS-10.0%2B-magenta.svg) [![License: MIT](https://img.shields.io/badge/License-MIT-indigo.svg)](https://opensource.org/licenses/MIT) ![Code Coverage](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/codefiesta/87655b6e3c89b9198287b2fefbfa641f/raw/oauthkit-coverage.json) @@ -36,11 +36,11 @@ Key features include: ## OAuthKit Installation -OAuthKit can be installed using [Swift Package Manager](https://www.swift.org/documentation/package-manager/). If you need to build with Swift Tools `6.1` and Apple APIs > `26.0` use version [1.5.1](https://github.com/codefiesta/OAuthKit/releases/tag/1.5.1). +OAuthKit can be installed using [Swift Package Manager](https://www.swift.org/documentation/package-manager/). ```swift dependencies: [ - .package(url: "https://github.com/codefiesta/OAuthKit", from: "2.0.1") + .package(url: "https://github.com/codefiesta/OAuthKit", from: "2.1.0") ] ``` diff --git a/Sources/OAuthKit/OAuth.swift b/Sources/OAuthKit/OAuth.swift index 6fd3339..00907f6 100644 --- a/Sources/OAuthKit/OAuth.swift +++ b/Sources/OAuthKit/OAuth.swift @@ -51,6 +51,18 @@ public final class OAuth: Sendable { #if os(macOS) || os(iOS) || os(visionOS) @ObservationIgnored var context: LAContext = .init() + + var policy: LAPolicy = { + #if os(macOS) || os(iOS) + if #available(macOS 15.0, iOS 18.0, *) { + return .deviceOwnerAuthenticationWithBiometricsOrCompanion + } + return .deviceOwnerAuthenticationWithBiometrics + #else + return .deviceOwnerAuthenticationWithBiometrics + #endif + }() + #endif @ObservationIgnored @@ -117,15 +129,15 @@ public extension OAuth { state = .authorizing(provider, grantType) case .deviceCode: state = .requestingDeviceCode(provider) - Task.immediate { + task { [self] in await requestDeviceCode(provider: provider) } case .clientCredentials: - Task.immediate { + task { [self] in await requestClientCredentials(provider: provider) } case .refreshToken: - Task.immediate { + task { [self] in await refreshToken(provider: provider) } } @@ -138,7 +150,7 @@ public extension OAuth { /// - code: the code to exchange /// - pkce: the pkce data func token(provider: Provider, code: String, pkce: PKCE? = nil) { - Task.immediate { + task { [self] in await requestToken(provider: provider, code: code, pkce: pkce) } } @@ -233,19 +245,12 @@ private extension OAuth { #if os(macOS) || os(iOS) || os(visionOS) let localizedReason = context.localizedReason.isNotEmpty ? context.localizedReason: defaultAuthenticationWithBiometricsOrCompanionReason - #if os(macOS) || os(iOS) - let policy: LAPolicy = .deviceOwnerAuthenticationWithBiometricsOrCompanion - #else - let policy: LAPolicy = .deviceOwnerAuthenticationWithBiometrics - #endif var error: NSError? if context.canEvaluatePolicy(policy, error: &error) { context.evaluatePolicy(policy, localizedReason: localizedReason) { [weak self] success, error in - guard let self else { return } - Task.immediate { @MainActor in - if success { - self.loadAuthorizations() - } + guard let self, success else { return } + Task { @MainActor [self] in + loadAuthorizations() } } } @@ -257,7 +262,7 @@ private extension OAuth { /// Starts the network monitor. func monitor() { - Task { + task { [self] in await networkMonitor.start() } } @@ -284,7 +289,7 @@ private extension OAuth { let timeInterval: TimeInterval = .init(deviceCode.interval) let task = Task.delayed(timeInterval: timeInterval) { [weak self] in guard let self else { return } - await self.poll(provider: provider, deviceCode: deviceCode) + await poll(provider: provider, deviceCode: deviceCode) } tasks.append(task) } @@ -304,18 +309,30 @@ private extension OAuth { // Schedule the auto refresh task let task = Task.delayed(timeInterval: timeInterval) { [weak self] in guard let self else { return } - await self.refreshToken(provider: provider) + await refreshToken(provider: provider) } tasks.append(task) } else { // Execute the task immediately - Task.immediate { + task { [self] in await refreshToken(provider: provider) } } } } } + + /// Create and immediately start running a new detached task in the context of this actor. + /// - Parameters: + /// - priority: the task priority + /// - operation: the operation to be run immediately upon entering the task. + func task(priority: TaskPriority = .high, operation: sending @escaping @isolated(any) () async throws -> Void) { + if #available(macOS 26, iOS 26, watchOS 26, tvOS 26, visionOS 26, *) { + Task.immediate(operation: operation) + } else { + Task(priority: priority, operation: operation) + } + } } // MARK: URLRequests