刷新 Spotify 令牌 iOS SDK 时遇到问题

2024-01-31

我对如何使用刷新令牌服务感到困惑。在我的应用程序中有一个包含许多播放列表的部分。当用户单击播放列表时,它会运行以下代码:

 func checkAuth() {
    print("checking auth")
    let auth = SPTAuth.defaultInstance()
    //print(auth!.session.isValid())
    if auth!.session == nil {
        print("no auth")
        if auth!.hasTokenRefreshService {
            print("refresh token if session == nil")
            self.renewTokenAndShowPlayer()
            return
        } else {
            self.performSegue(withIdentifier: "LoginControllerSegue", sender: nil)
        }
        return
    }
    if auth!.session.isValid() && firstLoad {
        // It's still valid, show the player.
        print("valid auth")
        self.showPlayer()
        return
    }

    if auth!.hasTokenRefreshService {
        print("refresh token")
        self.renewTokenAndShowPlayer()
        return
    }
}


    func renewTokenAndShowPlayer() {
    SPTAuth.defaultInstance().renewSession(SPTAuth.defaultInstance().session) { error, session in
        SPTAuth.defaultInstance().session = session
        if error != nil {
            print("Refreshing token failed.")
            print("*** Error renewing session: \(error)")
            self.performSegue(withIdentifier: "LoginControllerSegue", sender: nil)
            return
        }
        self.showPlayer()
    }
}

假设用户尚未登录,他们转到登录播放器,然后进行身份验证。

随后,当他们关闭播放器并单击不同的播放列表时,他们会再次进入登录屏幕。为什么是这样?

我相信我的刷新令牌服务有效,因为每当有人登录后调用它时,我的服务器就会得到一个/swap 200。此外,只有当有人返回应用程序(返回LoginViewController)登录Spotify后,这是为什么?

这是我的登录页面的代码:

import UIKit
import WebKit

class LoginViewController: UIViewController, SPTStoreControllerDelegate, WebViewControllerDelegate {

    @IBOutlet weak var statusLabel: UILabel!
    var authViewController: UIViewController?
    var firstLoad: Bool!
    var Information: [String:String]?

    override func viewDidLoad() {
        super.viewDidLoad()
        self.statusLabel.text = ""
        self.firstLoad = true

        let auth = SPTAuth.defaultInstance()


        NotificationCenter.default.addObserver(self, selector: #selector(self.sessionUpdatedNotification), name: NSNotification.Name(rawValue: "sessionUpdated"), object: nil)


        // Check if we have a token at all
        if auth!.session == nil {
            self.statusLabel.text = ""
            return
        }
        // Check if it's still valid
        if auth!.session.isValid() && self.firstLoad {
            // It's still valid, show the player.
            print("View did load, still valid, showing player")
            self.showPlayer()
            return
        }
        // Oh noes, the token has expired, if we have a token refresh service set up, we'll call tat one.
        self.statusLabel.text = "Token expired."
        print("Does auth have refresh service? \(auth!.hasTokenRefreshService)")
        if auth!.hasTokenRefreshService {
            print("trying to renew")
            self.renewTokenAndShowPlayer()
            return
        }

        // Else, just show login dialog
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

    }

    override var prefersStatusBarHidden: Bool {
        return true
    }

    func getAuthViewController(withURL url: URL) -> UIViewController {
        let webView = WebViewController(url: url)
        webView.delegate = self

        return UINavigationController(rootViewController: webView)
    }

    func sessionUpdatedNotification(_ notification: Notification) {
        self.statusLabel.text = ""
        let auth = SPTAuth.defaultInstance()
        self.presentedViewController?.dismiss(animated: true, completion: { _ in })
        if auth!.session != nil && auth!.session.isValid() {
            self.statusLabel.text = ""
            print("Session updated, showing player")
            self.showPlayer()
        }
        else {
            self.statusLabel.text = "Login failed."
            print("*** Failed to log in")
        }
    }

    func showPlayer() {
        self.firstLoad = false
        self.statusLabel.text = "Logged in."
        self.Information?["SpotifyUsername"] = SPTAuth.defaultInstance().session.canonicalUsername

        OperationQueue.main.addOperation {
            [weak self] in
            self?.performSegue(withIdentifier: "ShowPlayer", sender: self)
        }
        //self.performSegue(withIdentifier: "ShowPlayer", sender: nil)
    }

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "ShowPlayer" {
            if let destination = segue.destination as? PlayController {
                destination.Information = self.Information
            }
        }
    }

    internal func productViewControllerDidFinish(_ viewController: SPTStoreViewController) {
        self.statusLabel.text = "App Store Dismissed."
        viewController.dismiss(animated: true, completion: { _ in })
    }

    func openLoginPage() {
        self.statusLabel.text = "Logging in..."
        let auth = SPTAuth.defaultInstance()
        if SPTAuth.supportsApplicationAuthentication() {
            self.open(url: auth!.spotifyAppAuthenticationURL())
        } else {
            // storyboard?.instantiateViewController(withIdentifier: <#T##String#>)
            //
            self.authViewController = self.getAuthViewController(withURL: SPTAuth.defaultInstance().spotifyWebAuthenticationURL())
            self.definesPresentationContext = true
            self.present(self.authViewController!, animated: true, completion: { _ in })
        }
    }

    func open(url: URL) {
        if #available(iOS 10, *) {
            UIApplication.shared.open(url, options: [:],
                                      completionHandler: {
                                        (success) in
                                        print("Open \(url): \(success)")
            })
        } else {
            let success = UIApplication.shared.openURL(url)
            print("Open \(url): \(success)")
        }
    }

    func renewTokenAndShowPlayer() {
        self.statusLabel.text = "Refreshing token..."
        print("trying to renew")
        SPTAuth.defaultInstance().renewSession(SPTAuth.defaultInstance().session) { error, session in
            SPTAuth.defaultInstance().session = session
            if error != nil {
                self.statusLabel.text = "Refreshing token failed."
                print("*** Error renewing session: \(error)")
                return
            }
            print("refreshed token")
            self.presentedViewController?.dismiss(animated: true, completion: { _ in })
            self.showPlayer()
        }
    }

    func webViewControllerDidFinish(_ controller: WebViewController) {
        // User tapped the close button. Treat as auth error
        print("UI Web view did finish")
        let auth = SPTAuth.defaultInstance()
        // Uncomment to turn off native/SSO/flip-flop login flow
        //auth.allowNativeLogin = NO;
        // Check if we have a token at all
        if auth!.session == nil {
            self.statusLabel.text = ""
            return
        }
        // Check if it's still valid
        if auth!.session.isValid() && self.firstLoad {
            // It's still valid, show the player.
            print("Still valid, showing player")
            self.showPlayer()
            return
        }
    }

    @IBAction func loginButtonWasPressed(_ sender: SPTConnectButton) {
        self.openLoginPage()
    }

    @IBAction func showSpotifyAppStoreClicked(_ sender: UIButton) {
        self.statusLabel.text = "Presenting App Store..."
        let storeVC = SPTStoreViewController(campaignToken: "your_campaign_token", store: self)
        self.present(storeVC!, animated: true, completion: { _ in })
    }

    @IBAction func clearCookiesClicked(_ sender: UIButton) {
        let storage = HTTPCookieStorage.shared
        for cookie: HTTPCookie in storage.cookies! {
            if (cookie.domain as NSString).range(of: "spotify.").length > 0 || (cookie.domain as NSString).range(of: "facebook.").length > 0 {
                storage.deleteCookie(cookie)
            }
        }
        UserDefaults.standard.synchronize()
        self.statusLabel.text! = "Cookies cleared."
    }

    @IBAction func dismissViewController () {
        self.dismiss(animated: true, completion: {})
    }
}

这是我的 node.js 代码:

var spotifyEndpoint = 'https://accounts.spotify.com/api/token';

/**
 * Swap endpoint
 *
 * Uses an authentication code on req.body to request access and
 * refresh tokens. Refresh token is encrypted for safe storage.
 */
app.post('/swap', function (req, res, next) {
    var formData = {
            grant_type : 'authorization_code',
            redirect_uri : clientCallback,
            code : req.body.code
        },
        options = {
            uri : url.parse(spotifyEndpoint),
            headers : {
                'Authorization' : authorizationHeader
            },
            form : formData,
            method : 'POST',
            json : true
        };

    console.log("Options" + options);
    request(options, function (error, response, body) {
        if (response.statusCode === 200) {
            body.refresh_token = encrpytion.encrypt(body.refresh_token);
        } else {
          console.log("error swapping: " + error);
        }

        res.status(response.statusCode);
        res.json(body);
    });
});

app.post('/refresh', function (req, res, next) {
    if (!req.body.refresh_token) {
        res.status(400).json({ error : 'Refresh token is missing from body' });
        return;
    }

    var refreshToken = encrpytion.decrypt(req.body.refresh_token),
        formData = {
            grant_type : 'refresh_token',
            refresh_token : refreshToken
        },
        options = {
            uri : url.parse(spotifyEndpoint),
            headers : {
                'Authorization' : authorizationHeader
            },
            form : formData,
            method : 'POST',
            json : true
        };

    request(options, function (error, response, body) {
        if (response.statusCode === 200 && !!body.refresh_token) {
            body.refresh_token = encrpytion.encrypt(body.refresh_token);
        }

        res.status(response.statusCode);
        res.json(body);
    });
});

The LoginViewController仅在我登录一次后跳过,显示播放器,退出应用程序,然后再次启动应用程序并单击歌曲。为什么是这样?

(注意,这是我刷新令牌时遇到的错误:*** Error renewing session: Optional(Error Domain=com.spotify.auth Code=400 "No refresh token available in the session!" UserInfo={NSLocalizedDescription=No refresh token available in the session!}))

我从[这个]项目中获得了刷新代码。 Heroku 有必要吗?


None

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

刷新 Spotify 令牌 iOS SDK 时遇到问题 的相关文章

随机推荐

  • 返回 false 不停止表单提交

    我很确定这应该不会那么难 我有一个在提交时运行以下函数的表单 function FORMVALIDATE add rota entry var rota date rota date val var rota id rota id val
  • 自定义 Slack 机器人无法连接

    我一直在尝试制作一个 Slack 机器人 它可以回复简单的查询并做一些简单的事情来帮助办公室工作 该机器人工作正常 只是似乎无法从我们的代理后面连接到 Slack 当我从自己的移动互联网连接测试它时 它工作正常 但是当尝试在代理后面运行它时
  • Android 此功能需要 ASM7

    可能重复 https stackoverflow com questions 68709559 nestmember requires asm7 noredirect 1 我已将 Android Studio 更新为 Android Stu
  • 使用 automake 安装包含大量文件的数据目录树

    我有一个数据目录 我希望 automake 为其生成安装和卸载目标 本质上 我只想将此目录逐字复制到 DATA 目录 通常 我可能会单独列出所有文件 例如 dist whatever DATA dir subdir filea 但是当我的目
  • 如何在 spring Restapi 的 json 请求中的未知字段上抛出错误

    我有一个 spring Rest api 它获取 json 数据并绑定到 pojo GetData 每当我收到未知字段时 它都不会失败或抛出任何异常 我的要求是当它接收到 json 数据中的未知字段时应该抛出错误 public Respon
  • 如何实现 ILogger 将消息发送到 SignalR Hub?

    我想构建一个显示最新日志消息的 LogView 所以我构建了一个非常简单的设置 但在依赖注入方面失败了 这是我的实施尝试 我跳过了非关键部分 public class SignalRLogger ILogger private readon
  • 使用 Java 删除 JIRA 中的问题

    我正在尝试编写一种方法来删除 JIRA 中的问题 我已经有了创建和更新问题的方法 但我找不到任何有关如何使用 Java 删除问题的文档 如何从 Java 应用程序中删除 JIRA 问题 您可以尝试从 IssueService 中删除 htt
  • 如何限制谁可以 iframe 嵌入我的网络应用程序?

    限制 Web 应用程序可以嵌入 iframe 的网站的最佳 最安全 方法是什么 例如 所有不在名单上的人都应该被拒绝 www myFriend com www anotherfriend com www myThirdFriend com
  • 我应该使用什么类型来表示 C 枚举的二进制?

    据我所知 C 枚举是无符号整数 但这可能因实现而异 我应该为二进制表示形式的枚举使用什么类型 附注 二进制表示 是指字节数组 我想将枚举值序列化到套接字以与其他程序进行互操作 由编译器决定是否使用int代表一个enum类型 或一个long
  • Quartz.NET 服务器文档

    下载 Quartz NET 时 它包含一个带有控制台应用程序的服务器组件 网站 常见问题解答和 API 文档没有提及任何相关内容 有人知道更多关于它的信息或者知道我在哪里可以找到更多关于它的文档吗 谢谢 帕特里克 那么你可以加入Quartz
  • 在哪里可以找到使用 C# /// xml 文档注释的好示例? [关闭]

    Closed 此问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我正在寻找 NET XML 样式源代码注释和所有可用的各种标签的一些很好的示例 我在哪里可以找到一些好的
  • 如何使用 Windows + WSL2 在 Pycharm 中配置 Poetry 环境?

    TL DR 无法使用 WSL 中的现有 Poetry 环境在 PyCharm Windows 上配置 Python 解释器 当尝试设置 Poetry 环境路径时Add Python Interpreter gt Poetry Environ
  • GAE:使用 testbed 和 webtest 测试 blob 的下载

    我将 blobstore 与我的 Google App Engine 应用程序一起使用 并且在生产服务器和开发服务器上一切正常 使用测试台进行测试和webtest http webtest pythonpaste org 但是 不起作用 在
  • 检查 Discord 机器人是否在线

    我试图做到这一点 以便我的机器人一次只能连接到 Discord 而另一个仅在另一个未连接时才连接 我怎样才能做到这一点 我在用着不和谐 py https pypi org project discord py 另外 如果可能的话 我希望它能
  • 创建和使用带有代理对的字符串

    我必须使用上面的代码点0FFFF 特别是数学脚本字符 并且尚未找到有关如何执行此操作的简单教程 我希望能够 a 创建Strings 具有高代码点 并且 b 迭代其中的字符 自从char不能保留这些点我的代码如下所示 Test public
  • Colorbox 中的 CKEditor 加载不起作用 [ Google Chrome ]

    我在我的项目中使用 Colorbox 我已将 CKEditor 集成到 colorbox 中 它在所有浏览器中工作正常 但在 Google Chrome 中存在一个小问题 编辑器将在第一次单击时正确打开 关闭弹出窗口并在不加载页面的情况下第
  • 从插入触发器后调用存储过程

    也许是一个愚蠢的问题 如果我从插入后触发器 T SQL 调用存储过程 那么如何获取 刚刚插入 数据的值 例如 CREATE TRIGGER dbo MyTrigger ON dbo MyTable AFTER INSERT AS BEGIN
  • 将 NSString 转换为 cString 以与 CGContextShowTextAtPoint 一起使用

    我正在使用 CGContextShowTextAtPoint 绘制一个字符串 因此我需要将我想要绘制的 NSString 转换为 c 字符串 不幸的是 诸如欧元货币符号之类的特殊符号未正确显示 CGContextSelectFont cur
  • 合并霍夫线

    我的代码卡在了某一点 首先简短说明一下我正在做的事情 作为输入 有一张地板的图像 使用 Canny 和 HoughLinesP 算法 我想将整面墙分割成许多 小 部分 正如您在这里看到的那样 同时理想的输出 这里没有精明 我想得到 两条红线
  • 刷新 Spotify 令牌 iOS SDK 时遇到问题

    我对如何使用刷新令牌服务感到困惑 在我的应用程序中有一个包含许多播放列表的部分 当用户单击播放列表时 它会运行以下代码 func checkAuth print checking auth let auth SPTAuth defaultI