OAuth in Obsidian Plugins
Late last year I finally downloaded Obsidian, a note-taking app which starts out simple but can become quite powerful through its extensive plugin platform.
This year, as part of my goal of Digital Zen, I have been trying to use Obsidian more to keep all of my notes in one place, offline, and connected with all my devices. I have used a few different plugins including Dataview and even created my own for Letterboxd.
There are a few other services I want to connect to Obsidian now, and have been working on a new plugin to do that. Earlier this year, I decided to download Trakt, a service that lets me track the TV shows I watch and rate them. I hope that I’ll be able to use this information to help me recommend the best TV of the year.
There isn’t a Trakt plugin for Obsidian so I’ve been writing my own. The first step is to connect my Trakt account to Obsidian, which meant connecting to Trakt using OAuth.
While I haven’t gotten this plugin ready for launch yet, I did get OAuth working and am able to make queries to the API. I didn’t find a single “How to do OAuth in Obsidian” guide online, but I did find a few different hints from various posts on the web. So I figured I should write one.
OAuth Setup
First, I had to create an app through Trakt’s developer portal. I defined a few basic details about it. One thing to specify is the redirect uri should be obsidian://trakt-for-obsidian
.
For your own OAuth integration, you can pick a different path. It should be something distinct for your plugin, but it should always have the obsidian://
protocol. This will allow you to interface between Obsidian and the OAuth flow.
In your TypeScript app, make a note of the client and secret:
const TRAKT_CLIENT = ‘…’
const TRAKT_SECRET = ‘…’
Plugin OAuth Flow
As I’m using Trakt, I import the [Trakt Node.js library] (Todo) and then initialize it.
const Trakt = require(‘trakt.tv’)
const trakt = new Trakt({
client_id: TRAKT_CLIENT,
client_secret: TRAKT_SECRET,
redirect_uri: ‘obsidian://trakt-for-obsidian’,
})
In my plugin’s settings, I have defined a button to initialize the OAuth flow:
interface TraktSettings {
// Refresh Token
refresh?: string;
// …
}
new Setting(containerEl)
.addButton((component) => {
component.setButtonText(‘Authorize’)
component.onClick(() => {
const traktAuthUrl = trakt.get_url()
window.location.href = traktAuthUrl
})
})
You can see how I get the OAuth link for my app and then open that URL in the browser. In the screenshot above you can see a standard OAuth screen. When you click Allow, we need to kick you back to the Obsidian app and save the refresh token.
The plugin needs to register a handler for obsidian://trakt-for-obsidian
. When Allow is clicked, it will use the redirect_uri
defined earlier and include tokens. This can be done in the main app initialization.
this.registerObsidianProtocolHandler(‘trakt-for-obsidian’, async (data) => {
const {code, state} = data
const res = await trakt.exchange_code(code, state)
const {refresh_token, access_token} = res
this.settings.refresh = refresh_token
await this.saveSettings()
new Notice(‘You are now connected to Trakt’)
})
Trakt returns a code
and state
, which then can be exchanged for the refresh token. This can be saved to the plugin settings so that I can make further requests.
// Fetch *my* watched history
const allWatchedHistory: TraktWatchedShow[] = await trakt.sync.watched({ type: ‘shows’ })
// Do something with this value
Conclusion
There’s still a handful of different things I want to do in order to make this integration perfect. First, I want to adjust the buttons in the Settings depending on whether you’ve already connected or not. I also want to ensure this approach works on not just my PC but also on my phone.
But I did want to write down this smaller achievement for others but also for myself in the future. To reach digital zen I want to connect several services to Obsidian. As most services use OAuth to connect, I will return to my own guidance going forward once I’m ready to develop the next plugin.