diff --git a/package.json b/package.json
index 1e25213..a59aa7d 100644
--- a/package.json
+++ b/package.json
@@ -21,7 +21,7 @@
     "maxConcurrency": 1,
     "maxWorkers": 1,
     "testTimeout": 30000,
-    "testEnvironment": "node",
+    "testEnvironment": "jsdom",
     "transform": {
       "^.+\\.(t|j)sx?$": "ts-jest"
     },
diff --git a/src/sdk.ts b/src/sdk.ts
index 217eeee..f17312f 100644
--- a/src/sdk.ts
+++ b/src/sdk.ts
@@ -73,7 +73,7 @@ class Sdk {
     }
 
     public getSigninUrl(): string {
-        const redirectUri = `${window.location.origin}${this.config.redirectPath}`;
+        const redirectUri = this.config.redirectPath && this.config.redirectPath.includes('://') ? this.config.redirectPath : `${window.location.origin}${this.config.redirectPath}`;
         const scope = "read";
         const state = this.getOrSaveState();
         return `${this.config.serverUrl.trim()}/login/oauth/authorize?client_id=${this.config.clientId}&response_type=code&redirect_uri=${encodeURIComponent(redirectUri)}&scope=${scope}&state=${state}`;
diff --git a/test/sdk.test.ts b/test/sdk.test.ts
index dc08112..2c0c9a1 100644
--- a/test/sdk.test.ts
+++ b/test/sdk.test.ts
@@ -1,12 +1,75 @@
 import Sdk from '../src';
 
-test('new sdk', () => {
-    const sdkConfig = {
-        serverUrl: "https://door.casbin.com",
-        clientId: "014ae4bd048734ca2dea",
-        appName: "app-casnode",
-        organizationName: "casbin",
-        redirectPath: "/callback",
-    }
-    const sdk = new Sdk(sdkConfig)
-})
+const sdkConfig = {
+  serverUrl: 'https://door.casbin.com',
+  clientId: '014ae4bd048734ca2dea',
+  appName: 'app-casnode',
+  organizationName: 'casbin',
+  redirectPath: '/callback',
+};
+
+describe('sdk constructor', () => {
+  it('with full configs', () => {
+    const sdk = new Sdk(sdkConfig);
+
+    const instanceConfig = sdk['config'];
+    expect(instanceConfig.serverUrl).toEqual(sdkConfig.serverUrl);
+    expect(instanceConfig.clientId).toEqual(sdkConfig.clientId);
+    expect(instanceConfig.appName).toEqual(sdkConfig.appName);
+    expect(instanceConfig.organizationName).toEqual(sdkConfig.organizationName);
+    expect(instanceConfig.redirectPath).toEqual(sdkConfig.redirectPath);
+  });
+
+  it('config withou redirectPath', () => {
+    let config = {
+      ...sdkConfig,
+      redirectPath: undefined,
+    };
+    const sdk = new Sdk(sdkConfig);
+
+    const instanceConfig = sdk['config'];
+    expect(instanceConfig.redirectPath).toEqual('/callback');
+  });
+});
+
+describe('getSigninUrl', () => {
+  beforeEach(() => {
+    sessionStorage.clear();
+  });
+
+  it('redirectPath with relative path', () => {
+    const sdk = new Sdk(sdkConfig);
+
+    expect(sdk.getSigninUrl()).toContain(
+      `redirect_uri=${encodeURIComponent(
+        window.location.origin + sdkConfig.redirectPath
+      )}`
+    );
+  });
+
+  it('redirectPath with fully path', () => {
+    const config = {
+      ...sdkConfig,
+      redirectPath: 'http://localhost:6001/other-callback',
+    };
+    const sdk = new Sdk(config);
+
+    expect(sdk.getSigninUrl()).toContain(
+      `redirect_uri=${encodeURIComponent(config.redirectPath)}`
+    );
+  });
+
+  it('with fixed state', () => {
+    const state = 'test-state';
+    const sdk = new Sdk(sdkConfig);
+    sessionStorage.setItem('casdoor-state', state);
+    expect(sdk.getSigninUrl()).toContain(`state=${state}`);
+  });
+
+  it('with random state', () => {
+    const sdk = new Sdk(sdkConfig);
+    const url = sdk.getSigninUrl();
+    const state = sessionStorage.getItem('casdoor-state');
+    expect(url).toContain(`state=${state}`);
+  });
+});