Skip to content

Commit

Permalink
FEATURE: Request authentication token (#146)
Browse files Browse the repository at this point in the history
* Add Token model

* CreateRequetToken endpoint

* Add requestToken to AuthenticationService

* FEATURE: Request authentication token
  • Loading branch information
adamayoung authored Jan 24, 2024
1 parent 83b26dd commit 0cfbd7e
Show file tree
Hide file tree
Showing 89 changed files with 614 additions and 146 deletions.
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
}

}
File renamed without changes.
28 changes: 28 additions & 0 deletions Sources/TMDb/Services/Authentication/AuthenticateURLBuilding.swift
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
}

}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
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
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
4 changes: 3 additions & 1 deletion Sources/TMDb/TMDb.docc/Extensions/AuthenticationService.md
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

0 comments on commit 0cfbd7e

Please sign in to comment.