---
title: Uygulama access token'ı oluşturma
description: Uygulamanızın API'lerine erişimde kullanılacak token'ı ikas App Bridge veya imza doğrulamasıyla oluşturun.
---

Uygulamamızı bir mağazaya başarıyla yetkilendirdik. Şimdi uygulamamızın API'lerine erişirken kullanılacak bir access token oluşturmamız gerekiyor. Bu token'ı oluşturmanın en kolay yolu ikas App Bridge kullanmaktır. ikas App Bridge ile ikas Dashboard'dan, App Secret'ınızla şifrelenmiş bir JWT token isteyebilir ve bu token'ı server tarafında doğrulayabilirsiniz.

İkinci yöntem ise query parametrelerindeki imzayı (signature) dahili bir API ile doğrulamaktır. Satıcı uygulamanızı ikas Dashboard'da açtığında ikas, belirli query parametreleriyle uygulama URL'nize yönlendirir. Bu parametreleri App Secret'ınızla doğrulayabilirsiniz.

## ikas App Bridge ile Token Oluşturma

### `token-helpers.ts` Dosyasını Oluşturma

Önce ikas App Bridge kullanarak token yaşam döngüsünü yöneten bir helper class oluşturacağız.

- `lib` klasörü içinde `token-helpers.ts` adında bir dosya oluşturun.
- Ardından aşağıdaki kodu bu dosyaya yapıştırın.

```typescript title="File: lib/token-helpers.ts"
import { AppBridgeHelper } from '@ikas/app-helpers';
import { NextRouter } from 'next/router';
const TOKEN_KEY = 'token';
export class TokenHelpers {
  /**
   * You can only use this method if your app is displayed in IFrame within ikas dashboard
   * otherwise it won't retrieve token via AppBridge and throw a timeout error
   */
  static getTokenForIframeApp = async (router: NextRouter) => {
    let token = sessionStorage.getItem(TOKEN_KEY);
    if (token) {
      const tokenData = JSON.parse(atob(token.split('.')[1]));
      // Return token if it is not expired
      if (new Date().getTime() < tokenData.exp * 1000) return token;
    }
    // Check if token is inside an IFrame
    if (window.self !== window.top) {
      // Try to retrieve token from ikas dashboard via app bridge
      try {
        // If not found in session storage
        token = (await AppBridgeHelper.getNewToken()) || null;
        if (token) {
          // Save it to the session storage
          sessionStorage.setItem(TOKEN_KEY, token);
          return token;
        }
      } catch (e) {
        console.error(e);
      }
      await router.push('/authorize-store');
    }
    return;
  };
}
```

#### Kodun Açıklaması

```typescript
// Get token from session storage
let token = sessionStorage.getItem(TOKEN_KEY);
if (token) {
  const tokenData = JSON.parse(atob(token.split('.')[1]));
  // Return token if it is not expired
  if (new Date().getTime() < tokenData.exp * 1000) return token;
}
```

- Önce token'ın session storage'da olup olmadığını kontrol ediyoruz.
- Token varsa son kullanma tarihine bakıyor, süresi dolmamışsa token'ı döndürüyoruz.

```typescript
// Check if token is inside an IFrame
if (window.self !== window.top) {
  // If not found in session storage request new token from ikas
  token = (await AppBridgeHelper.getNewToken()) || null;
  if (token) {
    // Save it to the session storage
    sessionStorage.setItem(TOKEN_KEY, token);
    return token;
  }
}
```

- Token session storage'da yoksa veya süresi dolmuşsa ikas App Bridge'den yeni bir token almaya çalışıyoruz.
- Önce uygulamamızın bir iframe içinde yüklenip yüklenmediğini kontrol ediyoruz.
- Ardından `AppBridgeHelper.getNewToken()` fonksiyonunu çağırıyoruz. Bu fonksiyon ikas Dashboard'dan yeni bir token ister ve callback'i bekler. Callback geldiğinde alınan token'ı döndürür ve dinlemeyi bırakır. ikas App Bridge'e erişilemiyorsa fonksiyon bir timeout hatası fırlatır.
- Yeni token başarıyla alınırsa session storage'a kaydediyoruz.

### `app/page.tsx` Dosyasını Güncelleme

Artık `load` fonksiyonunu, ikas App Bridge'den yeni bir token isteyip ardından Dashboard sayfasına yönlendirecek şekilde güncelleyebiliriz.

- Önce `TokenHelpers.getTokenForIframeApp(router)` fonksiyonunu çağırarak yeni bir token istiyoruz.
- Token başarıyla alınırsa Dashboard sayfasına, alınamazsa mağaza yetkilendirme sayfasına yönlendiriyoruz.

Aşağıdaki kodu kullanabilirsiniz:

```typescript
const load = async () => {
  AppBridgeHelper.closeLoader();
  // Retrieve token from session storage or from ikas via AppBridge
  let token = await TokenHelpers.getTokenForIframeApp(router);
  if (token) {
    await router.push('/dashboard');
  } else {
    router.push('/authorize-store');
  }
}
```

Şimdi ikas Dashboard'a gidin, Uygulamalarım sayfasında uygulamanızı bulun ve açmak için tıklayın. Uygulamanız yüklendikten sonra başarı mesajını görüyorsanız uygulamanız ikas App Bridge'den yeni bir token'ı başarıyla almış demektir.

![ikas içindeki uygulamada başarı ekranı](https://builders.ikas.com/media/ikas-dev/app-page-success-29576867b3ce37690be0e757b22652f8.jpg)

## İmza Doğrulamasıyla Token Oluşturma

Harici (external) uygulamaların ikas App Bridge'e erişimi olmadığından, ikas'ın uygulama URL'nize eklediği query parametrelerini doğrulamanız gerekir. Satıcı Uygulamalarım veya App Store sayfasında uygulamanıza tıkladığında ikas, uygulama URL'nizi bulur, bu URL'ye `authorizedAppId`, `merchantId`, `signature`, `storeName` ve `timestamp` parametrelerini ekler ve kullanıcıyı bu adrese yönlendirir. Uygulamanız mount edildiğinde güvenlik açıklarını önlemek için `signature` parametresini doğrulayarak bu parametrelerin geçerliliğini kontrol etmeniz gerekir.

`signature` değerini doğrulamak için `${storeName}${merchantId}${timestamp}` değerinin App Secret'ınızla SHA256 hash'ini hesaplamanız ve sonucu `signature` ile karşılaştırmanız gerekir.

Referans olarak `signature` parametresini doğrulayan aşağıdaki örnek Node.js koduna göz atabilirsiniz:

```typescript
import crypto from 'crypto';
// Concatenate storeName, merchantId and timestamp params
const hashData = `${storeName}${merchantId}${timestamp}`;
// Convert timestamp to Date
const date = new Date(parseInt(timestamp));
// Calculate the date difference timestamp and now
const dateDiff = (new Date().getTime() - date.getTime()) / 1000;
// Calculate the signature with your appSecret
const calculatedSignature = crypto
  .createHmac('sha256', appSecret)
  .update(hashData)
  .digest('hex');
// Compare signatures and check if timestamp is not older than 2 minutes
return signature == calculatedSignature && dateDiff < 120;
```

Şimdi bu özelliği projemize ekleyelim.

### `get-token-with-signature.ts` Dosyasını Oluşturma

- Önce `signature` parametresini doğrulayan ve imza geçerliyse yeni bir JWT token döndüren yeni bir API oluşturacağız.
- `pages/api` klasörü içinde `get-token-with-signature.ts` adında bir dosya oluşturun ve aşağıdaki kodu yapıştırın.

```typescript title="File: pages/api/get-token-with-signature.ts"
import type { NextApiRequest, NextApiResponse } from 'next';
import { createRouter } from 'next-connect';
import { AuthConnectParams, validateAuthSignature } from '@ikas/api-client';
import { config } from '../../globals/config';
import { JwtHelpers } from '../../lib/jwt-helpers';
import { ApiErrorResponse } from '../../globals/types';

export type GetTokenWithSignatureApiRequest = AuthConnectParams;
export type GetTokenWithSignatureApiResponse = { token: string } | ApiErrorResponse;

const router = createRouter<NextApiRequest, NextApiResponse<GetTokenWithSignatureApiResponse>>();

/**
 * This API is implemented for external apps but can also be used by iframe apps.
 * Since external apps do not have access to AppBridge they require a different mechanism to identify stores.
 * When the merchant clicks your app from the ikas dashboard it adds some query variables to your app URL, and you should validate these variables with your app secret.
 */
router.post(async (req, res) => {
  try {
    // Validate the query parameters
    if (!req.body.authorizedAppId) res.status(400).json({ statusCode: 400, message: 'authorizedAppId is required' });
    if (!req.body.merchantId) res.status(400).json({ statusCode: 400, message: 'merchantId is required' });
    if (!req.body.signature) res.status(400).json({ statusCode: 400, message: 'signature is required' });
    if (!req.body.storeName) res.status(400).json({ statusCode: 400, message: 'storeName is required' });
    if (!req.body.timestamp) res.status(400).json({ statusCode: 400, message: 'timestamp is required' });
    const data = req.body as GetTokenWithSignatureApiRequest;
    // signature should be equal with sha256 hash of `${storeName}${merchantId}${timestamp}` string which is hashed by your appSecret
    const isValid = validateAuthSignature(data, config.appSecret);
    if (isValid) {
      res.json({ token: JwtHelpers.createToken(data.merchantId, data.authorizedAppId) });
    }
  } catch (err: any) {
    res.status(500).json({ statusCode: 500, message: err.message });
  }
});

export default router.handler({
  onError: (err: any, req, res) => {
    console.error(err.stack);
    res.status(err.statusCode || 500).end(err.message);
  },
});
```

- Bu API `authorizedAppId`, `merchantId`, `signature`, `storeName` ve `timestamp` parametrelerini alır ve imzayı `@ikas/api-client` kütüphanesinde hazır bulunan `validateAuthSignature` helper fonksiyonuyla doğrular.
- İmza geçerliyse bir JWT token oluşturup client tarafına döndürür. Projenizi `npx create-next-app myikas-app --example "https://github.com/ikascom/ikas-sample-app-nextjs/tree/initial"` komutuyla oluşturduysanız `JwtHelpers` class'ı projenizde zaten bulunuyor olmalıdır.

### `api-requests.ts` Dosyasını Oluşturma

Şimdi server tarafına yapılan tüm API isteklerini barındıracak bir helper obje oluşturacağız.

- `lib` klasörü içinde `api-requests.ts` adında bir dosya oluşturun ve aşağıdaki kodu yapıştırın.

```typescript
import axios from 'axios';
import { GetTokenWithSignatureApiRequest, GetTokenWithSignatureApiResponse } from '../pages/api/get-token-with-signature';
import { ApiErrorResponse } from '../globals/types';
/**
 * This is a helper object that holds all api requests that are used by the client-side.
 * This provides us to use strongly typed request and response data
 */
export const ApiRequests = {
  getTokenWithSignature: (data: GetTokenWithSignatureApiRequest) => makePostRequest<GetTokenWithSignatureApiResponse>({ url: '/api/get-token-with-signature', data }),
};
async function makePostRequest<T>({ url, data, token }: { url: string; data?: any; token?: string }) {
  return axios.post<T | ApiErrorResponse>(url, data, {
    headers: token
      ? {
          Authorization: `JWT ${token}`,
        }
      : undefined,
  });
}
async function makeGetRequest<T>({ url, data, token }: { url: string; data?: any; token?: string }) {
  return axios.get<T | ApiErrorResponse>(url, {
    params: data,
    headers: token
      ? {
          Authorization: `JWT ${token}`,
        }
      : undefined,
  });
}
```

### `token-helpers.ts` Dosyasını Güncelleme

Şimdi URL'deki query parametrelerini alıp `api/get-token-with-signature` API'sine gönderen bir fonksiyon yazalım. `lib/token-helpers.ts` dosyasını açın ve aşağıdaki fonksiyonu `TokenHelpers` class'ına ekleyin.

```typescript
/**
 *
 * This method only works if the URL has certain query parameters.
 * These params are generated by ikas and appended your App Url which is defined at Partner dashboard
 * Since the signature is encrypted by your appSecret you should validate it on the server-side
 *
 * @param router Next Router object
 * @param params extracted query params from 'window.location'
 */
static getTokenForExternalApp = async (router: NextRouter, params: URLSearchParams) => {
  if (params.has('storeName')) {
    if (
      params.has('merchantId') &&
      params.has('signature') &&
      params.has('authorizedAppId') &&
      params.has('timestamp')
    ) {
      const connectParams: AuthConnectParams = {
        authorizedAppId: params.get('authorizedAppId')!,
        merchantId: params.get('merchantId')!,
        signature: params.get('signature')!,
        storeName: params.get('storeName')!,
        timestamp: params.get('timestamp')!,
      };
      // Call `api/get-token-with-signature` with extracted parameters
      const res = await ApiRequests.getTokenWithSignature(connectParams);
      if (res.status == 200 && res.data?.hasOwnProperty('token')) {
        // @ts-ignore
        const token = res.data.token;
        // Save token to the session storage
        sessionStorage.setItem(TOKEN_KEY, token);
        return token;
      }
    }
    window.location.replace(`/api/oauth/authorize/ikas?storeName=${params.get('storeName')}`);
    return;
  }
  await router.push('/authorize-store');
};
```

### `app/page.ts` Dosyasını Güncelleme

`get-token-with-signature` API'sini oluşturduk ve `TokenHelpers.getTokenForExternalApp` fonksiyonu ile çağırdık. Şimdi yalnızca bunu ana sayfaya eklememiz gerekiyor.

Önce token'ı App Bridge ile almaya çalışıyoruz. Alamazsak `get-token-with-signature` API'si ile token oluşturmayı deniyoruz.

`app/page.ts` içindeki `load` fonksiyonunu aşağıdaki kodla güncelleyin.

```typescript
const load = async () => {
  AppBridgeHelper.closeLoader();
  const params = new URLSearchParams(window.location.search);
  // Retrieve token from session storage or from ikas via AppBridge
  let token = await TokenHelpers.getTokenForIframeApp(router);
  if (!token) {
    // App is not loaded in within ikas Dashboard try to load token via query params
    token = await TokenHelpers.getTokenForExternalApp(router, params);
  }
  if (token) {
    await router.push('/dashboard');
  }
}
```

Artık uygulamamız ikas Dashboard içinde yüklenmemiş olsa bile satıcı için token oluşturabiliriz.

### Access Token'ı Doğrulama

Anonim erişimi engellemek için uygulamamızın API'lerine yapılan her isteği doğrulamamız gerekir. Bunun için her istekten önce JWT token'ı doğrulayan ve token'ın süresi dolmuşsa ya da geçersizse `Unauthorized` hatası döndüren basit bir middleware oluşturuyoruz.

```typescript
import { NextApiRequest, NextApiResponse } from 'next';
import { JwtHelpers } from './jwt-helpers';
/**
 * This middleware extracts the JWT header and verifies it with your app secret.
 * Then embeds verified token data to `req` object
 *
 * @param req NextApiRequest object
 * @param res NextApiResponse object
 * @param next next function to inform Next.js server to run next middleware or API handler
 */
export const ensureLoggedIn = async (req: NextApiRequest, res: NextApiResponse, next: () => any) => {
  const authHeader = req.headers['authorization'];
  if (authHeader) {
    // Remove 'JWT ' prefix
    const token = authHeader.replace('JWT ', '');
    // Verify extracted token string
    const tokenData = JwtHelpers.verifyToken(token);
    if (tokenData) {
      // Embed the token data to req.user object
      req.user = {
        authorizedAppId: tokenData.aud as string,
        merchantId: tokenData.sub!,
      };
      return next();
    }
  }
  // if not verified or token does not exist return 401 error
  res.status(401).json({ statusCode: 401, message: 'Unauthorized' });
};
```

- Bu middleware, istekteki `Authorization` header'ını alır ve `appSecret` ile doğrular.
- Token geçerliyse kullanıcı bilgisini request objesine ekler ve bir sonraki middleware'e veya API handler'ına geçer.

Artık bu middleware'i handler'ımızla birlikte kullanabiliriz:

```typescript
const handler = nc<NextApiRequest, NextApiResponse<AddProductApiResponse | ApiErrorResponse>>()
  .use(ensureLoggedIn)
  .post(async (req, res) => {
  });
```
