Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release v3.2.0 #253

Merged
merged 15 commits into from
Jan 20, 2025
1 change: 1 addition & 0 deletions .RELEASE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Add Synology provider ([#248](https://github.com/pilcrowonpaper/arctic/pull/248)).
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ Arctic does not strictly follow semantic versioning. While we aim to only introd
- Spotify
- Start.gg
- Strava
- Synology
- TikTok
- Tiltify
- Tumblr
Expand Down
1 change: 1 addition & 0 deletions docs/malta.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
["Spotify", "/providers/spotify"],
["Start.gg", "/providers/startgg"],
["Strava", "/providers/strava"],
["Synology", "/providers/synology"],
["TikTok", "/providers/tiktok"],
["Tiltify", "/providers/tiltify"],
["Tumblr", "/providers/tumblr"],
Expand Down
87 changes: 87 additions & 0 deletions docs/pages/providers/synology.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
---
title: "Synology"
---

# Synology

OAuth 2.0 provider for Synology SSO and OAuth Apps.

Also see the [OAuth 2.0 with PKCE](/guides/oauth2-pkce) guide.

## Prerequisites

To use this provider, you have to install [SSO Server](https://www.synology.com/en-us/dsm/packages/SSOServer) package on your Synology NAS.

There you have to:

1. Configure the base URL under which the SSO Server will be reachable
2. Enable the OIDC service
3. Create a new OAuth App

> Note: Both the _base URL_ and the _redirect URI_ have to use `https`.

## Initialization

The `baseURL` parameter is the full URL of your Synology NAS that you have configured in the SSO Server.

```ts
import * as arctic from "arctic";

const baseURL = "https://my_synology_nas.local:5001";
const baseURL = "https://sso.nas.example.com";
const synology = new arctic.Synology(baseUrl, applicationId, applicationSecret, redirectURI);
```

## Create authorization URL

```ts
import * as arctic from "arctic";

const state = arctic.generateState();
const codeVerifier = arctic.generateCodeVerifier();
const scopes = ["email", "groups", "openid"];
const url = synology.createAuthorizationURL(state, codeVerifier, scopes);
```

> Note: You can find all available scopes in `/webman/sso/.well-known/openid-configuration`

## Validate authorization code

`validateAuthorizationCode()` will either return an [`OAuth2Tokens`](/reference/main/OAuth2Tokens), or throw one of [`OAuth2RequestError`](/reference/main/OAuth2RequestError), [`ArcticFetchError`](/reference/main/ArcticFetchError), [`UnexpectedResponseError`](/reference/main/UnexpectedResponseError), or [`UnexpectedErrorResponseBodyError`](/reference/main/UnexpectedErrorResponseBodyError).

Synology returns an access token and the access token expiration.

```ts
import * as arctic from "arctic";

try {
const tokens = await synology.validateAuthorizationCode(code, codeVerifier);
const accessToken = tokens.accessToken();
const accessTokenExpiresAt = tokens.accessTokenExpiresAt();
} catch (e) {
if (e instanceof arctic.OAuth2RequestError) {
// Invalid authorization code, credentials, or redirect URI
const code = e.code;
// ...
}
if (e instanceof arctic.ArcticFetchError) {
// Failed to call `fetch()`
const cause = e.cause;
// ...
}
// Parse error
}
```

## Get user info

Use the `/webman/sso/SSOUserInfo.cgi` endpoint.

```ts
const user_info = await fetch("https://example.com/webman/sso/SSOUserInfo.cgi", {
headers: {
Authorization: `Bearer ${accessToken}`
}
});
const user = await response.json();
```
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "arctic",
"type": "module",
"version": "3.1.3",
"version": "3.2.0",
"description": "OAuth 2.0 clients for popular providers",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export { Slack } from "./providers/slack.js";
export { Spotify } from "./providers/spotify.js";
export { StartGG } from "./providers/startgg.js";
export { Strava } from "./providers/strava.js";
export { Synology } from "./providers/synology.js";
export { TikTok } from "./providers/tiktok.js";
export { Tiltify } from "./providers/tiltify.js";
export { Tumblr } from "./providers/tumblr.js";
Expand Down
46 changes: 46 additions & 0 deletions src/providers/synology.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { CodeChallengeMethod, OAuth2Client } from "../client.js";
import { joinURIAndPath } from "../request.js";

import type { OAuth2Tokens } from "../oauth2.js";

export class Synology {
private authorizationEndpoint: string;
private tokenEndpoint: string;

private client: OAuth2Client;

constructor(
baseURL: string,
applicationId: string,
applicationSecret: string,
redirectURI: string
) {
this.authorizationEndpoint = joinURIAndPath(baseURL, "/webman/sso/SSOOauth.cgi");
this.tokenEndpoint = joinURIAndPath(baseURL, "/webman/sso/SSOAccessToken.cgi");

this.client = new OAuth2Client(applicationId, applicationSecret, redirectURI);
}

public createAuthorizationURL(state: string, codeVerifier: string, scopes: string[]): URL {
const url = this.client.createAuthorizationURLWithPKCE(
this.authorizationEndpoint,
state,
CodeChallengeMethod.S256,
codeVerifier,
scopes
);
return url;
}

public async validateAuthorizationCode(
code: string,
codeVerifier: string
): Promise<OAuth2Tokens> {
const tokens = await this.client.validateAuthorizationCode(
this.tokenEndpoint,
code,
codeVerifier
);
return tokens;
}
}