{"id":16863769,"url":"https://github.com/aidantwoods/swift-paseto","last_synced_at":"2025-03-22T06:32:15.379Z","repository":{"id":30363591,"uuid":"124013231","full_name":"aidantwoods/swift-paseto","owner":"aidantwoods","description":"Platform-Agnostic Security Tokens implementation in Swift","archived":false,"fork":false,"pushed_at":"2023-03-13T13:40:27.000Z","size":381,"stargazers_count":24,"open_issues_count":4,"forks_count":6,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-03-16T16:42:50.098Z","etag":null,"topics":["paseto","security","swift","token"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/aidantwoods.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-03-06T03:12:08.000Z","updated_at":"2025-01-08T13:20:31.000Z","dependencies_parsed_at":"2024-10-28T12:29:12.053Z","dependency_job_id":"28dc69cb-bc16-4d19-aa2d-3aa4df51b46a","html_url":"https://github.com/aidantwoods/swift-paseto","commit_stats":{"total_commits":213,"total_committers":4,"mean_commits":53.25,"dds":"0.22535211267605637","last_synced_commit":"811e2589a7eb65c16a39e64841034e11daa803a2"},"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aidantwoods%2Fswift-paseto","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aidantwoods%2Fswift-paseto/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aidantwoods%2Fswift-paseto/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aidantwoods%2Fswift-paseto/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aidantwoods","download_url":"https://codeload.github.com/aidantwoods/swift-paseto/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244918508,"owners_count":20531683,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["paseto","security","swift","token"],"created_at":"2024-10-13T14:39:41.185Z","updated_at":"2025-03-22T06:32:10.369Z","avatar_url":"https://github.com/aidantwoods.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Swift PASETO [![Build Status](https://travis-ci.org/aidantwoods/swift-paseto.svg?branch=master)](https://travis-ci.org/aidantwoods/swift-paseto)\n\nA Swift implementation of [PASETO](https://github.com/paragonie/paseto).\n\nPaseto is everything you love about JOSE (JWT, JWE, JWS) without any of the\n[many design deficits that plague the JOSE standards](https://paragonie.com/blog/2017/03/jwt-json-web-tokens-is-bad-standard-that-everyone-should-avoid).\n\n\n# Contents\n* [What is Paseto?](#what-is-paseto)\n  * [Key Differences between Paseto and JWT](#key-differences-between-paseto-and-jwt)\n* [Installation](#installation)\n  * [Requirements](#requirements)\n  * [Dependencies](#dependencies)\n* [Overview of the Swift library](#overview-of-the-swift-library)\n* [Supported Paseto Versions](#supported-paseto-versions)\n\n# What is Paseto?\n\n[Paseto](https://github.com/paragonie/paseto) (Platform-Agnostic SEcurity\nTOkens) is a specification for secure stateless tokens.\n\n## Key Differences between Paseto and JWT\n\nUnlike JSON Web Tokens (JWT), which gives developers more than enough rope with\nwhich to hang themselves, Paseto only allows secure operations. JWT gives you\n\"algorithm agility\", Paseto gives you \"versioned protocols\". It's incredibly\nunlikely that you'll be able to use Paseto in\n[an insecure way](https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries).\n\n\u003e **Caution:** Neither JWT nor Paseto were designed for\n\u003e [stateless session management](http://cryto.net/~joepie91/blog/2016/06/13/stop-using-jwt-for-sessions/).\n\u003e Paseto is suitable for tamper-proof cookies, but cannot prevent replay attacks\n\u003e by itself.\n\n# Installation\n\nUsing [Swift Package Manager](https://swift.org/package-manager/), add the\nfollowing to your `Package.swift`.\n\n```swift\ndependencies: [\n    .package(\n        url: \"https://github.com/aidantwoods/swift-paseto.git\",\n        .upToNextMajor(from: \"1.0.0\")\n    )\n]\n```\n\n# Overview of the Swift library\nThe Paseto Swift library is designed with the aim of using the Swift compiler to\ncatch as many usage errors as possible.\n\nAt some point, you the user will have to decide which key to use when using\nPaseto. As soon as you do this you effectively lock in two things: (i) the\nversion of Paseto tokens that you may use, (ii) the type of payload you\neither want to check or produce (i.e. encrypted if using local tokens,\nor signed if using public tokens).\n\nThe Paseto Swift library passes this information via type arguments (generics)\nso entire classes of misuse examples aren't possible (e.g. \ncreating a version 2 key and accidentally attempting to produce a version 1\ntoken, or trying to decrypt a signed token). In-fact, the functions that would\nenable you to even attempt these examples just don't exist.\n\nOkay, so what does all that look like?\n\nWhen creating a key, simply append the key type name to the version.\nLet's say we want to generate a new version 4 symmetric key:\n\n```swift\nlet symmetricKey = Version4.SymmetricKey()\n```\n\nOkay, now let's create a token:\n```swift\nvar token = Token(claims: [\n    \"data\":    \"this is a signed message\"\n])\n\n// set the expiry to 5 minutes from now\ntoken.expiration = Date() + 5 * 60\n```\n\nNow encrypt it:\n```swift\nguard let encrypted = try? token.encrypt(with: symmetricKey) else { /* respond to failure */ }\n```\n\nTo decrypt a token we need to parse it, and setup any validation rules we care about\n\n```swift\nvar parser = Parser\u003cVersion4.Local\u003e()\nguard let try? decryptedToken = parser.decrypt(encrypted, with: symmetricKey) else { /* respond to failure */ }\n```\n\nBy default, Parser will be initialised with a notExpired check. If you set your own rules\nin the constructor you can override this. If you just want to add new rules, you can use the\n`addRule` method without removing this default rule.\n\nLet's say we want to generate a new version 4 secret (private) key:\n```swift\nlet secretKey = Version4.AsymmetricSecretKey()\n```\n\nNow, if we wish produce a token which can be verified by others, we can\ndo the following:\n\n```swift\nlet publicKey = secretKey.publicKey // we need to save this so we can send it to others\nguard let signed = try? token.sign(with: secretKey) else { /* respond to failure */ }\n```\n\nTo verify a message `signed` with a public key, e.g. `1eb9dbbbbc047c03fd70604e0071f0987e16b28b757225c11f00415d0e20b1a2`\n\n```swift\nlet pkHex = \"1eb9dbbbbc047c03fd70604e0071f0987e16b28b757225c11f00415d0e20b1a2\"\nguard let publicKey = try? Version4.AsymmetricPublicKey(hex: pkHex) else { /* this will fail if key is invalid */ }\n\nvar parser = Parser\u003cVersion4.Public\u003e()\nguard let try? verifiedToken = parser.verify(signed, with: publicKey) else { /* respond to failure */ }\n```\n\nLastly, let's suppose that we do not start\nwith any objects. How do we create messages\nand keys from strings or data?\n\nLet's use the example from Paseto's test vectors:\n\nThe Paseto token is as follows (as a string/data)\n```\nv4.public.eyJkYXRhIjoidGhpcyBpcyBhIHNpZ25lZCBtZXNzYWdlIiwiZXhwIjoiMjAyMi0wMS0wMVQwMDowMDowMCswMDowMCJ9v3Jt8mx_TdM2ceTGoqwrh4yDFn0XsHvvV_D0DtwQxVrJEBMl0F2caAdgnpKlt4p7xBnx1HcO-SPo8FPp214HDw.eyJraWQiOiJ6VmhNaVBCUDlmUmYyc25FY1Q3Z0ZUaW9lQTlDT2NOeTlEZmdMMVc2MGhhTiJ9\n```\n\nAnd the symmetric key, given in hex is:\n```\n1eb9dbbbbc047c03fd70604e0071f0987e16b28b757225c11f00415d0e20b1a2\n```\n\nTo produce a token, use the following:\n\n```swift\nlet rawToken = \"v4.public.eyJkYXRhIjoidGhpcyBpcyBhIHNpZ25lZCBtZXNzYWdlIiwiZXhwIjoiMjAyMi0wMS0wMVQwMDowMDowMCswMDowMCJ9v3Jt8mx_TdM2ceTGoqwrh4yDFn0XsHvvV_D0DtwQxVrJEBMl0F2caAdgnpKlt4p7xBnx1HcO-SPo8FPp214HDw.eyJraWQiOiJ6VmhNaVBCUDlmUmYyc25FY1Q3Z0ZUaW9lQTlDT2NOeTlEZmdMMVc2MGhhTiJ9\"\n\nguard let key = try? Version4.AsymmetricPublicKey(\n    hex: \"1eb9dbbbbc047c03fd70604e0071f0987e16b28b757225c11f00415d0e20b1a2\"\n) else {\n    /* respond to failure */\n}\n\nvar parser = Parser\u003cVersion4.Public\u003e(rules: []) // setting rules to empty to remove expiry check:\n                                                // this is only necessary for demonstration purposes because this token has expired\nguard let token = try? parser.verify(rawToken, with: key) else {\n    /* respond to failure */\n}\n\n// the following will succeed\nassert(token.claims == [\"data\": \"this is a signed message\", \"exp\": \"2022-01-01T00:00:00+00:00\"])\nassert(token.footer == \"{\\\"kid\\\":\\\"zVhMiPBP9fRf2snEcT7gFTioeA9COcNy9DfgL1W60haN\\\"}\")\n```\n\nKeys can also be created using url safe base64 (with no padding) using\n`init(encoded: String)` or with the raw key material as data\nby using `init(material: Data)`.\n\nIf you need to determine the type of a received raw token, you can use the\nhelper function `Util.header(of: String) -\u003e Header?` to retrieve a `Header`\ncorresponding to the given token. This only checks that the given string\nis of a valid format, and does not guarantee anything about the contents.\n\nFor example, using `rawToken` from above:\n```swift\nguard let header = Util.header(of: rawToken) else { /* this isn't a valid Paseto token */ }\n```\n\nA `Header` is of the following structure:\n```swift\nstruct Header {\n    let version: Version\n    let purpose: Purpose\n}\n```\n\nwhere `version` is either `.v1`, `.v2`, `.v3`, or `.v4`, and `purpose` is either `.Public` (a\nsigned message) or `.Local` (an encrypted message).\n\nAs `Version` and `Purpose` are enums, it is recommended that you use an\nexplicitly exhaustive (i.e. no default) switch-case construct to select\ndifferent code paths. Making this explicitly exhaustive ensures that if, say\nadditional versions are added then the Swift compiler will inform you when you\nhave not considered all possibilities.\n\nIf you attempt to create a message using a raw token which produces a header that\ndoes not correspond to the message's type arguments then the initialiser will fail.\n\n# Supported Paseto Versions\n## Version 4\nVersion 4 is fully supported.\n\n## Version 3\nVersion 3 is fully supported.\n\n\u003e Note: Support for public mode requires `@available(macOS 11, iOS 14, watchOS 7, tvOS 14, macCatalyst 14, *)`.\n\n## Version 2\nVersion 2 is fully supported.\n\n## Version 1 (partial)\nVersion 1 (the compatibility version) is (ironically) only partially supported\ndue to compatibility issues (Swift is a new language 🤷‍♂️).\n\nVersion 1 in the local mode (i.e. encrypted payloads using symmetric keys) is\nfully supported.\nVersion 1 in the public mode (i.e. signed payloads using asymmetric keys) is\n**not** currently supported.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faidantwoods%2Fswift-paseto","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faidantwoods%2Fswift-paseto","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faidantwoods%2Fswift-paseto/lists"}