Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FEATURE: Request authentication token #146

Merged
merged 4 commits into from
Jan 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 0 additions & 71 deletions Sources/TMDb/Authentication/AuthenticationService.swift

This file was deleted.

4 changes: 4 additions & 0 deletions Sources/TMDb/Extensions/URL+TMDb.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,8 @@ extension URL {
URL(string: "https://api.themoviedb.org/3")!
}

static var tmdbWebSiteURL: URL {
URL(string: "https://www.themoviedb.org")!
}

}
46 changes: 46 additions & 0 deletions Sources/TMDb/Helpers/AuthenticateURLBuilder.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
//
// AuthenticateURLBuilder.swift
// TMDb
//
// Copyright © 2024 Adam Young.
//
// 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.
//

import Foundation

final class AuthenticateURLBuilder: AuthenticateURLBuilding {

private let baseURL: URL

init(baseURL: URL) {
self.baseURL = baseURL
}

func authenticateURL(with requestToken: String) -> URL {
authenticateURL(with: requestToken, redirectURL: nil)
}

func authenticateURL(with requestToken: String, redirectURL: URL?) -> URL {
var url = baseURL
.appendingPathComponent("authenticate")
.appendingPathComponent(requestToken)

if let redirectURL {
url = url.appendingQueryItem(name: "redirect_to", value: redirectURL.absoluteString)
}

return url
}

}
56 changes: 56 additions & 0 deletions Sources/TMDb/Models/Token.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
//
// Token.swift
// TMDb
//
// Copyright © 2024 Adam Young.
//
// 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.
//

import Foundation

///
/// A model representing an internediate request token.
///
public struct Token: Codable, Equatable, Hashable {

///
/// Was token creation successful.
///
public let success: Bool

///
/// An intermediate request token.
///
public let requestToken: String

///
/// Date of token expiry.
///
public let expiresAt: Date

///
/// Creates an internediate request token.
///
/// - Parameters:
/// - success: Was token creation successful.
/// - requestToken: An intermediate request token.
/// - expiresAt: Date of token expiry.
///
public init(success: Bool, requestToken: String, expiresAt: Date) {
self.success = success
self.requestToken = requestToken
self.expiresAt = expiresAt
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//
// AuthenticateURLBuilding.swift
// TMDb
//
// Copyright © 2024 Adam Young.
//
// 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.
//

import Foundation

protocol AuthenticateURLBuilding {

func authenticateURL(with requestToken: String) -> URL

func authenticateURL(with requestToken: String, redirectURL: URL?) -> URL

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import Foundation
enum AuthenticationEndpoint {

case createGuestSession
case createRequestToken

}

Expand All @@ -35,6 +36,11 @@ extension AuthenticationEndpoint: Endpoint {
Self.basePath
.appendingPathComponent("guest_session")
.appendingPathComponent("new")

case .createRequestToken:
Self.basePath
.appendingPathComponent("token")
.appendingPathComponent("new")
}
}

Expand Down
116 changes: 116 additions & 0 deletions Sources/TMDb/Services/Authentication/AuthenticationService.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
//
// AuthenticationService.swift
// TMDb
//
// Copyright © 2024 Adam Young.
//
// 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.
//

import Foundation

///
/// Provides an interface for authenticating and generating session IDs with TMDb.
///
/// Details of generating session IDs for TMDb can be found at
/// [TMDb API - How do I generate a session ID?](https://developer.themoviedb.org/reference/authentication-how-do-i-generate-a-session-id)
///
@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
public final class AuthenticationService {

private let apiClient: any APIClient
private let authenticateURLBuilder: any AuthenticateURLBuilding

///
/// Creates an authentication service object.
///
public convenience init() {
self.init(
apiClient: TMDbFactory.authAPIClient,
authenticateURLBuilder: TMDbFactory.authenticateURLBuilder
)
}

init(
apiClient: some APIClient,
authenticateURLBuilder: some AuthenticateURLBuilding
) {
self.apiClient = apiClient
self.authenticateURLBuilder = authenticateURLBuilder
}

///
/// Creates a guest session with TMDb.
///
/// Guest sessions are a special kind of session that give you some of the functionality of an account, but not
/// all. For example, some of the things you can do with a guest session are; maintain a rated list, a watchlist
/// and a favourite list.
///
/// Guest sessions will automatically be deleted if they are not used within 60 minutes of it being issued.
///
/// [TMDb API - Authentication: Create Guest Session](https://developer.themoviedb.org/reference/authentication-create-guest-session)
///
/// - Throws: TMDb error ``TMDbError``.
///
/// - Returns: A guest session.
///
public func guestSession() async throws -> GuestSession {
let session: GuestSession
do {
session = try await apiClient.get(endpoint: AuthenticationEndpoint.createGuestSession)
} catch let error {
throw TMDbError(error: error)
}

return session
}

///
/// Creates an intermediate request token that can be used to validate a TMDb user login.
///
/// This is a temporary token that is required to ask the user for permission to access their account. This token
/// will auto expire after 60 minutes if it's not used.
///
/// [TMDb API - Authentication: Create Request Token](https://developer.themoviedb.org/reference/authentication-create-request-token)
///
/// - Returns: An intermediate request token.
///
public func requestToken() async throws -> Token {
let token: Token
do {
token = try await apiClient.get(endpoint: AuthenticationEndpoint.createRequestToken)
} catch let error {
throw TMDbError(error: error)
}

return token
}

///
/// Builds the URL used for the user to authenticate with after requesting an intermediate request token.
///
/// An internediate request token can be generated by calling ``requestToken()``.
///
/// - Parameters:
/// - token: An intermediate request token.
/// - redirectURL: Optional URL to redirect to once the user has authenticated.
///
/// - Returns: An authenticate URL.
///
public func authenticateURL(for token: Token, redirectURL: URL? = nil) -> URL {
let url = authenticateURLBuilder.authenticateURL(with: token.requestToken, redirectURL: redirectURL)

return url
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,7 @@ public final class MovieService {
/// Returns watch providers for a movie
///
/// [TMDb API - Movie: Watch providers](https://developer.themoviedb.org/reference/movie-watch-providers)
///
/// - Parameters:
/// - id: The identifier of the movie.
///
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,6 @@

### Creating Sessions

- ``createGuestSession()``
- ``guestSession()``
- ``requestToken()``
- ``authenticateURL(for:redirectURL:)``
Loading
Loading