Skip to content

Comments

feat: initial commit#1

Merged
sirily11 merged 1 commit intomainfrom
app
Feb 8, 2026
Merged

feat: initial commit#1
sirily11 merged 1 commit intomainfrom
app

Conversation

@sirily11
Copy link
Contributor

@sirily11 sirily11 commented Feb 8, 2026

No description provided.

Copilot AI review requested due to automatic review settings February 8, 2026 06:21
@sirily11 sirily11 enabled auto-merge (squash) February 8, 2026 06:21
@autopilot-project-manager autopilot-project-manager bot added the enhancement New feature or request label Feb 8, 2026
@sirily11 sirily11 merged commit e1e4ea9 into main Feb 8, 2026
6 checks passed
@sirily11 sirily11 deleted the app branch February 8, 2026 06:22
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Initial commit of the RxAuthSwift Swift Package, providing a core OAuth 2.0 + PKCE authentication manager (iOS/macOS) and an optional SwiftUI sign-in UI layer, along with build scripts and CI.

Changes:

  • Add core RxAuthSwift module: configuration, PKCE helpers, OAuth manager, keychain token storage, and platform web auth sessions.
  • Add RxAuthSwiftUI module: sign-in view, appearance configuration, and supporting UI components/modifiers.
  • Add tests, documentation, build scripts, and a GitHub Actions CI workflow.

Reviewed changes

Copilot reviewed 27 out of 29 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
scripts/build-macos.sh macOS build + test script using SwiftPM.
scripts/build-ios.sh iOS build script using xcodebuild.
Tests/RxAuthSwiftTests/RxAuthSwiftTests.swift Unit tests for PKCE/config/user model/errors.
Sources/RxAuthSwiftUI/Styles/GlassEffectModifiers.swift SwiftUI “glass” styling modifiers and fallbacks.
Sources/RxAuthSwiftUI/RxSignInView.swift Main sign-in view wiring OAuthManager to UI.
Sources/RxAuthSwiftUI/RxSignInAppearance.swift Appearance configuration model for the sign-in UI.
Sources/RxAuthSwiftUI/Components/SecondaryAuthButton.swift Secondary action button component.
Sources/RxAuthSwiftUI/Components/PrimaryAuthButton.swift Primary sign-in button with loading state.
Sources/RxAuthSwiftUI/Components/AuthErrorBanner.swift Error banner UI with animation/haptics.
Sources/RxAuthSwiftUI/Components/AnimatedSecurityIcon.swift Animated security icon component.
Sources/RxAuthSwiftUI/Components/AnimatedGradientBackground.swift Animated gradient/orb background.
Sources/RxAuthSwiftUI/Components/AnimatedAppLogo.swift Animated logo/icon header component.
Sources/RxAuthSwift/TokenStorageProtocol.swift Protocol for access/refresh token persistence.
Sources/RxAuthSwift/RxAuthSwift.swift Module entry/export convenience imports.
Sources/RxAuthSwift/RxAuthNotifications.swift Notification for session expiry.
Sources/RxAuthSwift/RxAuthConfiguration.swift OAuth configuration and endpoint URL construction.
Sources/RxAuthSwift/Platform/WebAuthSessionProvider.swift Internal abstraction for platform web auth sessions.
Sources/RxAuthSwift/Platform/MacOSWebAuthSession.swift macOS WKWebView-based authentication session.
Sources/RxAuthSwift/Platform/IOSWebAuthSession.swift iOS ASWebAuthenticationSession implementation.
Sources/RxAuthSwift/PKCEHelper.swift PKCE verifier/challenge generation.
Sources/RxAuthSwift/OAuthManager.swift Main OAuth flow, token exchange/refresh, userinfo fetch.
Sources/RxAuthSwift/OAuthError.swift Error types for OAuth/keychain operations.
Sources/RxAuthSwift/KeychainTokenStorage.swift Keychain-backed token storage implementation.
Sources/RxAuthSwift/AuthenticationState.swift Auth state enum and User model.
README.md Usage documentation, setup, and examples.
Package.swift SwiftPM package definition (targets/products/deps).
Package.resolved Locked dependency resolution (swift-log).
.gitignore Ignore build artifacts and local config files.
.github/workflows/ci.yml CI jobs to build/test macOS and build iOS.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +96 to +100
public func refreshTokenIfNeeded() async throws {
guard let refreshToken = tokenStorage.getRefreshToken() else {
throw OAuthError.noRefreshToken
}

Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

refreshTokenIfNeeded() refreshes any time a refresh token exists, even when the access token is still valid. With the repeating timer, this will trigger unnecessary refreshes and can cause rate limiting/refresh-token invalidation; add an early return when !tokenStorage.isTokenExpired() (and an access token exists).

Copilot uses AI. Check for mistakes.
Comment on lines +169 to +175
let body = [
"grant_type=authorization_code",
"code=\(code)",
"redirect_uri=\(configuration.redirectURI)",
"client_id=\(configuration.clientID)",
"code_verifier=\(codeVerifier)",
].joined(separator: "&")
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This x-www-form-urlencoded token-exchange body is built via string interpolation without percent-encoding values such as code, redirect_uri, and code_verifier. Use proper form-url-encoding (e.g., URLQueryItem percent-encoding) to avoid malformed requests.

Suggested change
let body = [
"grant_type=authorization_code",
"code=\(code)",
"redirect_uri=\(configuration.redirectURI)",
"client_id=\(configuration.clientID)",
"code_verifier=\(codeVerifier)",
].joined(separator: "&")
var components = URLComponents()
components.queryItems = [
URLQueryItem(name: "grant_type", value: "authorization_code"),
URLQueryItem(name: "code", value: code),
URLQueryItem(name: "redirect_uri", value: "\(configuration.redirectURI)"),
URLQueryItem(name: "client_id", value: configuration.clientID),
URLQueryItem(name: "code_verifier", value: codeVerifier),
]
let body = components.percentEncodedQuery ?? ""

Copilot uses AI. Check for mistakes.
Comment on lines +27 to +31
public enum SignInIcon: Sendable {
case systemImage(String)
case image(Image)
case assetImage(String, Bundle?)
case none
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SignInIcon is marked Sendable but has a case that stores a SwiftUI Image, which is typically non-Sendable. This can break compilation with strict concurrency; consider replacing the Image payload with a sendable identifier (asset/system name) or dropping Sendable.

Copilot uses AI. Check for mistakes.
Comment on lines +28 to +32
nonisolated func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor {
MainActor.assumeIsolated {
guard let scene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
let window = scene.windows.first
else {
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UIApplication/UIWindowScene are used below, but this file only imports AuthenticationServices. This will fail to compile on iOS unless you add import UIKit (inside the same #if os(iOS) guard) or otherwise avoid UIKit symbols here.

Copilot uses AI. Check for mistakes.
nonisolated func webView(
_ webView: WKWebView,
decidePolicyFor navigationAction: WKNavigationAction,
decisionHandler: @escaping @MainActor @Sendable (WKNavigationActionPolicy) -> Void
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The WKNavigationDelegate signature must match WebKit exactly. Adding @MainActor @Sendable to the decisionHandler parameter changes the closure type and can break protocol conformance/compilation; keep the original signature and hop to the main actor inside as needed.

Suggested change
decisionHandler: @escaping @MainActor @Sendable (WKNavigationActionPolicy) -> Void
decisionHandler: @escaping (WKNavigationActionPolicy) -> Void

Copilot uses AI. Check for mistakes.
Comment on lines +109 to +115
let body = [
"grant_type=refresh_token",
"refresh_token=\(refreshToken)",
"client_id=\(configuration.clientID)",
].joined(separator: "&")
request.httpBody = body.data(using: .utf8)

Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This x-www-form-urlencoded body is built via string interpolation without percent-encoding values like refresh_token and client_id. If these contain reserved characters, the request will be malformed; build the body using proper form-url-encoding.

Suggested change
let body = [
"grant_type=refresh_token",
"refresh_token=\(refreshToken)",
"client_id=\(configuration.clientID)",
].joined(separator: "&")
request.httpBody = body.data(using: .utf8)
var components = URLComponents()
components.queryItems = [
URLQueryItem(name: "grant_type", value: "refresh_token"),
URLQueryItem(name: "refresh_token", value: refreshToken),
URLQueryItem(name: "client_id", value: configuration.clientID)
]
request.httpBody = components.percentEncodedQuery?.data(using: .utf8)

Copilot uses AI. Check for mistakes.
public enum PKCEHelper: Sendable {
public static func generateCodeVerifier() -> String {
var bytes = [UInt8](repeating: 0, count: 32)
_ = SecRandomCopyBytes(kSecRandomDefault, bytes.count, &bytes)
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The return status from SecRandomCopyBytes is ignored. If it fails, a weak/invalid PKCE verifier could be produced silently; check the OSStatus and fail/throw/retry instead of proceeding.

Suggested change
_ = SecRandomCopyBytes(kSecRandomDefault, bytes.count, &bytes)
let status = SecRandomCopyBytes(kSecRandomDefault, bytes.count, &bytes)
guard status == errSecSuccess else {
fatalError("Failed to generate secure random bytes for PKCE code verifier. OSStatus: \(status)")
}

Copilot uses AI. Check for mistakes.
Comment on lines +3 to +5
public struct RxSignInAppearance: Sendable {
public var icon: SignInIcon
public var title: String
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RxSignInAppearance is declared Sendable, but it contains SwiftUI types (e.g., Color, and Image via SignInIcon) that are not guaranteed to satisfy strict Swift 6 sendability checks. Consider removing Sendable or storing sendable identifiers instead of SwiftUI values.

Copilot uses AI. Check for mistakes.

@MainActor
@Observable
public final class OAuthManager: Sendable {
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OAuthManager conforms to Sendable but includes stored properties like Timer that are not Sendable. This is likely to fail strict concurrency checking; either remove Sendable (often fine for a @MainActor observable object) or make it @unchecked Sendable with a safety justification.

Suggested change
public final class OAuthManager: Sendable {
public final class OAuthManager {

Copilot uses AI. Check for mistakes.
Comment on lines +65 to +79
let codeVerifier = PKCEHelper.generateCodeVerifier()
let codeChallenge = PKCEHelper.generateCodeChallenge(from: codeVerifier)

guard let authorizeURL = buildAuthorizationURL(codeChallenge: codeChallenge) else {
throw OAuthError.invalidConfiguration
}

guard let callbackScheme = configuration.redirectScheme else {
throw OAuthError.invalidConfiguration
}

logger.info("Starting OAuth authentication flow")

let callbackURL = try await platformAuthenticate(url: authorizeURL, callbackScheme: callbackScheme)
try await handleCallback(url: callbackURL, codeVerifier: codeVerifier)
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

errorMessage is cleared at the start of authenticate(), but it’s never set when authentication fails. Since the SwiftUI view swallows the thrown error and relies on manager.errorMessage, users will never see an error banner; set errorMessage in failure paths (and decide whether to still throw).

Suggested change
let codeVerifier = PKCEHelper.generateCodeVerifier()
let codeChallenge = PKCEHelper.generateCodeChallenge(from: codeVerifier)
guard let authorizeURL = buildAuthorizationURL(codeChallenge: codeChallenge) else {
throw OAuthError.invalidConfiguration
}
guard let callbackScheme = configuration.redirectScheme else {
throw OAuthError.invalidConfiguration
}
logger.info("Starting OAuth authentication flow")
let callbackURL = try await platformAuthenticate(url: authorizeURL, callbackScheme: callbackScheme)
try await handleCallback(url: callbackURL, codeVerifier: codeVerifier)
do {
let codeVerifier = PKCEHelper.generateCodeVerifier()
let codeChallenge = PKCEHelper.generateCodeChallenge(from: codeVerifier)
guard let authorizeURL = buildAuthorizationURL(codeChallenge: codeChallenge) else {
throw OAuthError.invalidConfiguration
}
guard let callbackScheme = configuration.redirectScheme else {
throw OAuthError.invalidConfiguration
}
logger.info("Starting OAuth authentication flow")
let callbackURL = try await platformAuthenticate(url: authorizeURL, callbackScheme: callbackScheme)
try await handleCallback(url: callbackURL, codeVerifier: codeVerifier)
} catch {
logger.error("Authentication failed: \(error.localizedDescription)")
errorMessage = error.localizedDescription
throw error
}

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant