Reputation: 1068
I have a JWT token like this
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
How can I decode this so that I can get the payload like this
{ "sub": "1234567890", "name": "John Doe", "admin": true }
Upvotes: 45
Views: 45795
Reputation: 4013
in SwiftUI you can use this:
Working in XCode 14.2(14C18), iOS 15
func decodeJwt(from jwt: String) -> String {
let segments = jwt.components(separatedBy: ".")
var base64String = segments[1]
let requiredLength = Int(4 * ceil(Float(base64String.count) / 4.0))
let nbrPaddings = requiredLength - base64String.count
if nbrPaddings > 0 {
let padding = String().padding(toLength: nbrPaddings, withPad: "=", startingAt: 0)
base64String = base64String.appending(padding)
}
base64String = base64String.replacingOccurrences(of: "-", with: "+")
base64String = base64String.replacingOccurrences(of: "_", with: "/")
let decodedData = Data(base64Encoded: base64String, options: Data.Base64DecodingOptions(rawValue: UInt(0)))
let base64Decoded: String = String(data: decodedData! as Data, encoding: String.Encoding(rawValue: String.Encoding.utf8.rawValue))!
return base64Decoded
}
Upvotes: 0
Reputation: 3591
I refactored @Viktor Gardart code
Use like this
let token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJhY2NvdW56IiwianRpIjoiZTA0NGEyMTAtZjVmZi00Yjc2LWI2MzMtNTk0NjYzMWE0MjRjLWQxYTc3bXlpdGE0YnZnaG4yd2YiLCJleHAiOjE2NDk2NDI3MTF9.FO-AQhZ18qogsSbeTUY78EqhfL9xp9iUG3OlpOdxemE"
let jsonWebToken = JSONWebToken(jsonWebToken: token)
let expirationTime = jsonWebToken?.payload.expirationTime
JSONWebToken.swift
import Foundation
struct JSONWebToken {
let header: JSONWebTokenHeader
let payload: JSONWebTokenPayload
let signature: String
}
extension JSONWebToken {
init?(jsonWebToken: String) {
let encodedData = { (string: String) -> Data? in
var encodedString = string.replacingOccurrences(of: "-", with: "+").replacingOccurrences(of: "_", with: "/")
switch (encodedString.utf16.count % 4) {
case 2: encodedString = "\(encodedString)=="
case 3: encodedString = "\(encodedString)="
default: break
}
return Data(base64Encoded: encodedString)
}
let components = jsonWebToken.components(separatedBy: ".")
guard components.count == 3,
let headerData = encodedData(components[0] as String),
let payloadData = encodedData(components[1] as String) else { return nil }
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
do {
header = try decoder.decode(JSONWebTokenHeader.self, from: headerData)
payload = try decoder.decode(JSONWebTokenPayload.self, from: payloadData)
signature = components[2] as String
} catch {
print(error.localizedDescription)
return nil
}
}
}
JSONWebTokenHeader.swift
import Foundation
struct JSONWebTokenHeader {
let type: String
let algorithm: String
}
extension JSONWebTokenHeader: Codable {
private enum Key: String, CodingKey {
case type = "typ"
case algorithm = "alg"
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: Key.self)
do { try container.encode(type, forKey: .type) } catch { throw error }
do { try container.encode(algorithm, forKey: .algorithm) } catch { throw error }
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: Key.self)
do { type = try container.decode(String.self, forKey: .type) } catch { throw error }
do { algorithm = try container.decode(String.self, forKey: .algorithm) } catch { throw error }
}
}
JSONWebTokenPayload.swift
import Foundation
struct JSONWebTokenPayload {
let issuer: String
let expirationTime: Double
let jsonWebTokenID: String
}
extension JSONWebTokenPayload: Codable {
private enum Key: String, CodingKey {
case issuer = "iss"
case expirationTime = "exp"
case jsonWebTokenID = "jti"
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: Key.self)
do { try container.encode(issuer, forKey: .issuer) } catch { throw error }
do { try container.encode(expirationTime, forKey: .expirationTime) } catch { throw error }
do { try container.encode(jsonWebTokenID, forKey: .jsonWebTokenID) } catch { throw error }
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: Key.self)
do { issuer = try container.decode(String.self, forKey: .issuer) } catch { throw error }
do { expirationTime = try container.decode(Double.self, forKey: .expirationTime) } catch { throw error }
do { jsonWebTokenID = try container.decode(String.self, forKey: .jsonWebTokenID) } catch { throw error }
}
}
Upvotes: 3
Reputation: 9266
Iterating on Viktor's code:
Hope it is useful:
func decode(jwtToken jwt: String) throws -> [String: Any] {
enum DecodeErrors: Error {
case badToken
case other
}
func base64Decode(_ base64: String) throws -> Data {
let base64 = base64
.replacingOccurrences(of: "-", with: "+")
.replacingOccurrences(of: "_", with: "/")
let padded = base64.padding(toLength: ((base64.count + 3) / 4) * 4, withPad: "=", startingAt: 0)
guard let decoded = Data(base64Encoded: padded) else {
throw DecodeErrors.badToken
}
return decoded
}
func decodeJWTPart(_ value: String) throws -> [String: Any] {
let bodyData = try base64Decode(value)
let json = try JSONSerialization.jsonObject(with: bodyData, options: [])
guard let payload = json as? [String: Any] else {
throw DecodeErrors.other
}
return payload
}
let segments = jwt.components(separatedBy: ".")
return try decodeJWTPart(segments[1])
}
Upvotes: 52
Reputation: 4196
Objective-C version for oldies:
NSLog(@"credential is %@", credential.identityToken);
NSString * string = [[NSString alloc] initWithData:credential.identityToken encoding:NSUTF8StringEncoding];
NSArray * segments = [string componentsSeparatedByString:@"."];
NSMutableDictionary * JSON = [NSMutableDictionary new];
for (int n = 0; n < segments.count; n++){
NSString * value = segments[n];
NSString * base64 = [value stringByReplacingOccurrencesOfString:@"-" withString:@"+"];
base64 = [base64 stringByReplacingOccurrencesOfString:@"_" withString:@"/"];
NSUInteger length = [base64 lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
int requiredLength = 4 * ceil((float)length/4.0f);
int paddingLength = requiredLength - (int)length;
for (int n = 0; n < paddingLength; n++){
base64 = [base64 stringByAppendingString:@"="];
}
NSData * data = [[NSData alloc] initWithBase64EncodedString:base64 options:0];
NSError * error;
NSDictionary * local = [NSJSONSerialization JSONObjectWithData:data
options:NSJSONReadingAllowFragments
error:&error];
[JSON addEntriesFromDictionary:local];
}
NSLog(@"JSON is %@", JSON);
Upvotes: 4
Reputation: 1904
I you want to use a library for that I recommend using something popular from someone big. IBM is making Kitura – Swift backend framework, so the implementation of encoding and decoding JWT for it must be top notch:
Link: https://github.com/IBM-Swift/Swift-JWT
Simple usage for token with expiry date
import SwiftJWT
struct Token: Decodable {
let jwtString: String
func abc() {
do {
let newJWT = try JWT<MyJWTClaims>(jwtString: jwtString)
print(newJWT.claims.exp)
} catch {
print("OH NOES")
}
}
}
struct MyJWTClaims: Claims {
let exp: Date
}
Upvotes: 4
Reputation: 459
func decode(_ token: String) -> [String: AnyObject]? {
let string = token.components(separatedBy: ".")
let toDecode = string[1] as String
var stringtoDecode: String = toDecode.replacingOccurrences(of: "-", with: "+") // 62nd char of encoding
stringtoDecode = stringtoDecode.replacingOccurrences(of: "_", with: "/") // 63rd char of encoding
switch (stringtoDecode.utf16.count % 4) {
case 2: stringtoDecode = "\(stringtoDecode)=="
case 3: stringtoDecode = "\(stringtoDecode)="
default: // nothing to do stringtoDecode can stay the same
print("")
}
let dataToDecode = Data(base64Encoded: stringtoDecode, options: [])
let base64DecodedString = NSString(data: dataToDecode!, encoding: String.Encoding.utf8.rawValue)
var values: [String: AnyObject]?
if let string = base64DecodedString {
if let data = string.data(using: String.Encoding.utf8.rawValue, allowLossyConversion: true) {
values = try! JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments) as? [String : AnyObject]
}
}
return values
}
Upvotes: 10
Reputation: 4490
If you are okay with using a library i would suggest this https://github.com/auth0/JWTDecode.swift
and then import the library import JWTDecode
and execute.
let jwt = try decode(jwt: token)
Since you didn't want to include this library i brought out the needed parts to make it work.
func decode(jwtToken jwt: String) -> [String: Any] {
let segments = jwt.components(separatedBy: ".")
return decodeJWTPart(segments[1]) ?? [:]
}
func base64UrlDecode(_ value: String) -> Data? {
var base64 = value
.replacingOccurrences(of: "-", with: "+")
.replacingOccurrences(of: "_", with: "/")
let length = Double(base64.lengthOfBytes(using: String.Encoding.utf8))
let requiredLength = 4 * ceil(length / 4.0)
let paddingLength = requiredLength - length
if paddingLength > 0 {
let padding = "".padding(toLength: Int(paddingLength), withPad: "=", startingAt: 0)
base64 = base64 + padding
}
return Data(base64Encoded: base64, options: .ignoreUnknownCharacters)
}
func decodeJWTPart(_ value: String) -> [String: Any]? {
guard let bodyData = base64UrlDecode(value),
let json = try? JSONSerialization.jsonObject(with: bodyData, options: []), let payload = json as? [String: Any] else {
return nil
}
return payload
}
Call it like this:
decode(jwtToken: TOKEN)
Upvotes: 92
Reputation: 1068
I have got the solution for this.
static func getJwtBodyString(tokenstr: String) -> NSString {
var segments = tokenstr.components(separatedBy: ".")
var base64String = segments[1]
print("\(base64String)")
let requiredLength = Int(4 * ceil(Float(base64String.characters.count) / 4.0))
let nbrPaddings = requiredLength - base64String.characters.count
if nbrPaddings > 0 {
let padding = String().padding(toLength: nbrPaddings, withPad: "=", startingAt: 0)
base64String = base64String.appending(padding)
}
base64String = base64String.replacingOccurrences(of: "-", with: "+")
base64String = base64String.replacingOccurrences(of: "_", with: "/")
let decodedData = Data(base64Encoded: base64String, options: Data.Base64DecodingOptions(rawValue: UInt(0)))
// var decodedString : String = String(decodedData : nsdata as Data, encoding: String.Encoding.utf8)
let base64Decoded: String = String(data: decodedData! as Data, encoding: String.Encoding(rawValue: String.Encoding.utf8.rawValue))!
print("\(base64Decoded)")
return base64String as NSString
}
This works for me great. Thank you.
Upvotes: 8
Reputation: 837
There's a swift implementation. Add this into your Podfile if you're using CocoaPods or clone the project and use it directly.
do {
// the token that will be decoded
let token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ"
let payload = try JWT.decode(token, algorithm: .hs256("secret".data(using: .utf8)!))
print(payload)
} catch {
print("Failed to decode JWT: \(error)")
}
Upvotes: 3