Skip to content
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
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# TransloaditKit Changelog

## 3.4
## 3.5.0

* Allow clients to inject only an api key and provide a signature generator closure to calculate signatures for signing requests instead of injecting a key and secret. ([#42](https://github.com/transloadit/TransloaditKit/issues/42))

## 3.4.0

* Updated Package to depend on exact TUSKit version and removed call to removed method in TUSKit ([#41](https://github.com/transloadit/TransloaditKit/issues/41))

Expand Down
42 changes: 42 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,53 @@ dependencies: [

Start by initializing `Transloadit`.

### Simple initialization; pass key and secret to the SDK

```swift
let credentials = Transloadit.Credentials(key: "SomeKey", secret: "SomeSecret")
let transloadit = Transloadit(credentials: credentials, session: URLSession.shared)
```

Certain transloadit endpoints (can) require signatures to be included in their requests. The SDK can automatically generate signatures on your behalf but this requires you to pass both your Transloadit key _and_ secret to the SDK.

The SDK does not persist your secret locally beyond the SDK's lifetime.

This means that you're free to obtain your SDK secret in a secure manner from an external host or that you can include it in your app binary. It's up to you.

It's also possible to initialize the SDK with a `nil` secret and manage signing yourself.

### Advanced initialization; omit secret for manual request signing

If, for security reasons, you choose to not expose your API secret to the app in any way, shape, or form, you can manage signature generation yourself. This allows you to generate signatures on your server and provide them to Transloadit as needed.

To do this, use the `Transloadit` initializer that takes an api key and a `signatureGenerator`

```swift
let transloadit = Transloadit(
apiKey: "YOUR-API-KEY",
sessionConfiguration: .default,
signatureGenerator: { stringToSign, onSignatureGenerated in
mySigningService.sign(stringToSign) { result in
onSignatureGenerated(result)
}
})
```

The signature generator is defined as follows:

```swift
public typealias SignatureCompletion = (Result<String, Error>) -> Void
public typealias SignatureGenerator = (String, SignatureCompletion) -> Void
```

The generator itself is passed a string that needs to be signed (a JSON representation of the request parameters that you're generating a signature for) and a closure that you _must_ call to inform the SDK when you're done generating the signature (whether it's successful or failed).

**Important** if you don't call the completion handler, your requests will never be sent. The SDK does not implement a fallback or timeout.

The SDK will invoke the signature generator for every request that requires a signature. It will pass a parameter string for each request to your closure which you can then send to your service (local or external) for signature generation.

To learn more about signature generation see this page: https://transloadit.com/docs/api/authentication/

### Create an Assembly

To create an `Assembly` you invoke `createAssembly(steps:andUpload:completion)` on `Transloadit`.
Expand Down
67 changes: 62 additions & 5 deletions Sources/TransloaditKit/Transloadit.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@ import Foundation

/// The errors that `Transloadit` can return
public enum TransloaditError: Error {

case couldNotFetchStatus(underlyingError: Error)
case couldNotCreateAssembly(underlyingError: Error)
case couldNotUploadFile(underlyingError: Error)
case couldNotClearCache(underlyingError: Error)
}

public enum SDKConfigurationError: Error {
case missingClientSecret
}

public protocol TransloaditFileDelegate: AnyObject {

func didStartUpload(assembly: Assembly, client: Transloadit)
Expand All @@ -31,6 +34,9 @@ public protocol TransloaditFileDelegate: AnyObject {
func didError(error: Error, client: Transloadit)
}

public typealias SignatureCompletion = (Result<String, Error>) -> Void
public typealias SignatureGenerator = (String, SignatureCompletion) -> Void

/// Use the `Transloadit` class to upload files using the underlying TUS protocol.
/// You can either create an Assembly by itself, or create an Assembly and upload files to it right away.
///
Expand All @@ -42,9 +48,9 @@ public final class Transloadit {

public struct Credentials {
let key: String
let secret: String
let secret: String?

public init(key: String, secret: String) {
public init(key: String, secret: String?) {
self.key = key
self.secret = secret
}
Expand Down Expand Up @@ -84,7 +90,18 @@ public final class Transloadit {
/// then TUS will make a directory, whether one you specify or a default one in the documents directory.
@available(*, deprecated, message: "Use the new init(credentials:sessionConfig:storageDir:) instead.")
public init(credentials: Transloadit.Credentials, session: URLSession, storageDir: URL? = nil) {
self.api = TransloaditAPI(credentials: credentials, session: session)
self.api = TransloaditAPI(
credentials: credentials,
session: session,
signatureGenerator: { parameterString, generationComplete in
guard let secret = credentials.secret, !secret.isEmpty else {
generationComplete(.failure(SDKConfigurationError.missingClientSecret))
return
}

generationComplete(.success("sha384:" + parameterString.hmac(key: secret)))
}
)
self.storageDir = storageDir
self.tusSessionConfig = session.configuration.copy(withIdentifier: "com.transloadit.tus.bg")
}
Expand All @@ -97,7 +114,47 @@ public final class Transloadit {
/// If left empty, no directory will be made when performing non-file related tasks, such as creating assemblies. However, if you start uploading files,
/// then TUS will make a directory, whether one you specify or a default one in the documents directory.
public init(credentials: Transloadit.Credentials, sessionConfiguration: URLSessionConfiguration, storageDir: URL? = nil) {
self.api = TransloaditAPI(credentials: credentials, sessionConfiguration: sessionConfiguration)
self.api = TransloaditAPI(
credentials: credentials,
sessionConfiguration: sessionConfiguration,
signatureGenerator: { parameterString, generationComplete in
guard let secret = credentials.secret, !secret.isEmpty else {
generationComplete(.failure(SDKConfigurationError.missingClientSecret))
return
}

generationComplete(.success("sha384:" + parameterString.hmac(key: secret)))
}
)
self.storageDir = storageDir
self.tusSessionConfig = sessionConfiguration.copy(withIdentifier: "com.transloadit.tus.bg")
}

/// Initialize Transloadit without a secret, providing a signature generator.
/// - Parameters:
/// - apiKey: Transloadit API key.
/// - sessionConfiguration: A URLSessionConfiguration to use.
/// - storageDir: A storagedirectory to use. Used by underlying TUSKit mechanism to store files.
/// If left empty, no directory will be made when performing non-file related tasks, such as creating assemblies. However, if you start uploading files,
/// then TUS will make a directory, whether one you specify or a default one in the documents directory.
/// - signatureGenerator: A closure that's invoked to generate the signature for the API request. Implement your own logic to generate a valid
/// signature. Call the provided completion handler with your signed string or an error as needed.
///
/// For example, you can make a request to your backend to generate the signature for you. The closure is passed a string that holds all request params
/// that need to be signed. See https://transloadit.com/docs/api/authentication/ for more information on signature authentication.
/// The closure is invoked by the TransloaditAPI when needed.
///
/// ** Important:** It's up to the caller to ensure that all codepaths (eventually) call the completion handler. The SDK does not implement any timeouts or fallbacks.
public init(
apiKey: String, sessionConfiguration: URLSessionConfiguration,
storageDir: URL? = nil, signatureGenerator: @escaping SignatureGenerator
) {
let credentials = Transloadit.Credentials(key: apiKey, secret: nil)
self.api = TransloaditAPI(
credentials: credentials,
sessionConfiguration: sessionConfiguration,
signatureGenerator: signatureGenerator
)
self.storageDir = storageDir
self.tusSessionConfig = sessionConfiguration.copy(withIdentifier: "com.transloadit.tus.bg")
}
Expand Down
Loading
Loading