OAuth 2.0 এবং Java এর জন্য Google OAuth ক্লায়েন্ট লাইব্রেরি

ওভারভিউ

উদ্দেশ্য: এই নথিটি জাভার জন্য Google OAuth ক্লায়েন্ট লাইব্রেরি দ্বারা অফার করা জেনেরিক OAuth 2.0 ফাংশনগুলি বর্ণনা করে৷ আপনি যেকোনো ইন্টারনেট পরিষেবার জন্য প্রমাণীকরণ এবং অনুমোদনের জন্য এই ফাংশনগুলি ব্যবহার করতে পারেন।

Google পরিষেবাগুলির সাথে OAuth 2.0 অনুমোদন করতে GoogleCredential ব্যবহার করার নির্দেশাবলীর জন্য, Java এর জন্য Google API ক্লায়েন্ট লাইব্রেরির সাথে OAuth 2.0 ব্যবহার করা দেখুন৷

সারাংশ: OAuth 2.0 হল একটি প্রমিত স্পেসিফিকেশন যা শেষ ব্যবহারকারীদের সুরক্ষিত সার্ভার-সাইড রিসোর্স অ্যাক্সেস করার জন্য একটি ক্লায়েন্ট অ্যাপ্লিকেশনকে নিরাপদে অনুমোদন করার অনুমতি দেয়। উপরন্তু, OAuth 2.0 ধারক টোকেন স্পেসিফিকেশন ব্যাখ্যা করে যে কীভাবে শেষ-ব্যবহারকারী অনুমোদন প্রক্রিয়ার সময় প্রদত্ত অ্যাক্সেস টোকেন ব্যবহার করে সেই সুরক্ষিত সংস্থানগুলিতে অ্যাক্সেস করা যায়।

বিস্তারিত জানার জন্য, নিম্নলিখিত প্যাকেজগুলির জন্য Javadoc ডকুমেন্টেশন দেখুন:

ক্লায়েন্ট নিবন্ধন

Java এর জন্য Google OAuth ক্লায়েন্ট লাইব্রেরি ব্যবহার করার আগে, আপনাকে সম্ভবত একটি ক্লায়েন্ট আইডি এবং ক্লায়েন্ট গোপনীয়তা পাওয়ার জন্য একটি অনুমোদন সার্ভারের সাথে আপনার অ্যাপ্লিকেশন নিবন্ধন করতে হবে৷ (এই প্রক্রিয়া সম্পর্কে সাধারণ তথ্যের জন্য, ক্লায়েন্ট রেজিস্ট্রেশন স্পেসিফিকেশন দেখুন।)

শংসাপত্র এবং শংসাপত্রের দোকান

শংসাপত্র হল একটি অ্যাক্সেস টোকেন ব্যবহার করে সুরক্ষিত সংস্থানগুলি অ্যাক্সেস করার জন্য একটি থ্রেড-নিরাপদ OAuth 2.0 সহায়ক শ্রেণী। রিফ্রেশ টোকেন ব্যবহার করার সময়, রিফ্রেশ টোকেন ব্যবহার করে অ্যাক্সেস টোকেনের মেয়াদ শেষ হয়ে গেলে Credential অ্যাক্সেস টোকেনটিকেও রিফ্রেশ করে। উদাহরণস্বরূপ, যদি আপনার কাছে ইতিমধ্যে একটি অ্যাক্সেস টোকেন থাকে তবে আপনি নিম্নলিখিত উপায়ে একটি অনুরোধ করতে পারেন:

  public static HttpResponse executeGet(
      HttpTransport transport, JsonFactory jsonFactory, String accessToken, GenericUrl url)
      throws IOException {
    Credential credential =
        new Credential(BearerToken.authorizationHeaderAccessMethod()).setAccessToken(accessToken);
    HttpRequestFactory requestFactory = transport.createRequestFactory(credential);
    return requestFactory.buildGetRequest(url).execute();
  }

বেশিরভাগ অ্যাপ্লিকেশনের ক্রেডেনশিয়ালের অ্যাক্সেস টোকেন এবং রিফ্রেশ টোকেন বজায় রাখতে হবে যাতে ভবিষ্যতে ব্রাউজারে অনুমোদন পৃষ্ঠায় পুনঃনির্দেশ না হয়। এই লাইব্রেরিতে ক্রেডেনশিয়ালস্টোর বাস্তবায়ন অবমূল্যায়ন করা হয়েছে এবং ভবিষ্যতের রিলিজে অপসারণ করা হবে। বিকল্প হল স্টোরডক্রিডেনশিয়ালের সাথে DataStoreFactory এবং DataStore ইন্টারফেস ব্যবহার করা, যা Java এর জন্য Google HTTP ক্লায়েন্ট লাইব্রেরি দ্বারা সরবরাহ করা হয়।

আপনি লাইব্রেরি দ্বারা প্রদত্ত নিম্নলিখিত বাস্তবায়নের একটি ব্যবহার করতে পারেন:

  • JdoDataStoreFactory JDO ব্যবহার করে শংসাপত্র বজায় রাখে।
  • AppEngineDataStoreFactory Google অ্যাপ ইঞ্জিন ডেটা স্টোর API ব্যবহার করে শংসাপত্র বজায় রাখে।
  • MemoryDataStoreFactory মেমরিতে শংসাপত্রকে "স্থির রাখে", যা প্রক্রিয়াটির জীবনকালের জন্য শুধুমাত্র একটি স্বল্পমেয়াদী স্টোরেজ হিসাবে কার্যকর।
  • FileDataStoreFactory একটি ফাইলে শংসাপত্র বজায় রাখে।

গুগল অ্যাপ ইঞ্জিন ব্যবহারকারী:

AppEngineCredentialStore বাতিল করা হয়েছে এবং সরানো হচ্ছে।

আমরা সুপারিশ করি যে আপনি AppEngineDataStoreFactory ব্যবহার করুন StoredCredential- এর সাথে। যদি আপনার কাছে পুরানো পদ্ধতিতে শংসাপত্রগুলি সংরক্ষিত থাকে, আপনি মাইগ্রেট করার জন্য যোগ করা সহায়ক পদ্ধতিগুলি migrateTo(AppEngineDataStoreFactory) বা migrateTo(DataStore) ব্যবহার করতে পারেন৷

DataStoreCredentialRefreshListener ব্যবহার করুন এবং GoogleCredential.Builder.addRefreshListener(CredentialRefreshListener) ব্যবহার করে শংসাপত্রের জন্য এটি সেট করুন।

অনুমোদন কোড প্রবাহ

শেষ ব্যবহারকারীকে তাদের সুরক্ষিত ডেটাতে আপনার অ্যাপ্লিকেশন অ্যাক্সেস দেওয়ার অনুমতি দেওয়ার জন্য অনুমোদন কোড প্রবাহ ব্যবহার করুন। এই প্রবাহের প্রোটোকল অনুমোদন কোড অনুদান স্পেসিফিকেশনে নির্দিষ্ট করা হয়েছে।

এই প্রবাহটি AuthorizationCodeFlow ব্যবহার করে প্রয়োগ করা হয়। ধাপগুলো হল:

  • একটি শেষ ব্যবহারকারী আপনার অ্যাপ্লিকেশন লগ ইন. আপনাকে সেই ব্যবহারকারীকে একটি ব্যবহারকারী আইডির সাথে যুক্ত করতে হবে যা আপনার অ্যাপ্লিকেশনের জন্য অনন্য।
  • ব্যবহারকারীর পরিচয়পত্র ইতিমধ্যে পরিচিত কিনা তা পরীক্ষা করতে ব্যবহারকারীর আইডির উপর ভিত্তি করে AuthorizationCodeFlow.loadCredential(String) এ কল করুন। যদি তাই হয়, আপনি সম্পন্ন.
  • যদি না হয়, AuthorizationCodeFlow.newAuthorizationUrl() কল করুন এবং শেষ ব্যবহারকারীর ব্রাউজারকে একটি অনুমোদন পৃষ্ঠায় নির্দেশ করুন যেখানে তারা আপনার অ্যাপ্লিকেশনটিকে তাদের সুরক্ষিত ডেটাতে অ্যাক্সেস দিতে পারে৷
  • ওয়েব ব্রাউজার তারপর একটি "কোড" ক্যোয়ারী প্যারামিটার সহ রিডাইরেক্ট URL-এ পুনঃনির্দেশ করে যা তারপর AuthorizationCodeFlow.newTokenRequest(String) ব্যবহার করে একটি অ্যাক্সেস টোকেন অনুরোধ করতে ব্যবহার করা যেতে পারে।
  • সংরক্ষিত সংস্থানগুলি অ্যাক্সেস করার জন্য একটি শংসাপত্র সংরক্ষণ এবং প্রাপ্ত করতে AuthorizationCodeFlow.createAndStoreCredential(TokenResponse, String) ব্যবহার করুন।

বিকল্পভাবে, আপনি যদি AuthorizationCodeFlow ব্যবহার না করেন, তাহলে আপনি নিম্ন-স্তরের ক্লাস ব্যবহার করতে পারেন:

  • ব্যবহারকারী আইডির উপর ভিত্তি করে স্টোর থেকে শংসাপত্র লোড করতে DataStore.get(String) ব্যবহার করুন।
  • অনুমোদন পৃষ্ঠায় ব্রাউজারকে নির্দেশ করতে AuthorizationCodeRequestUrl ব্যবহার করুন।
  • অনুমোদনের প্রতিক্রিয়া প্রক্রিয়া করতে এবং অনুমোদন কোড পার্স করতে AuthorizationCodeResponseUrl ব্যবহার করুন।
  • একটি অ্যাক্সেস টোকেন এবং সম্ভবত একটি রিফ্রেশ টোকেন অনুরোধ করতে AuthorizationCodeTokenRequest ব্যবহার করুন।
  • একটি নতুন শংসাপত্র তৈরি করুন এবং DataStore.set(String, V) ব্যবহার করে এটি সংরক্ষণ করুন।
  • শংসাপত্র ব্যবহার করে সুরক্ষিত সম্পদ অ্যাক্সেস করুন। মেয়াদোত্তীর্ণ অ্যাক্সেস টোকেন স্বয়ংক্রিয়ভাবে রিফ্রেশ টোকেন ব্যবহার করে রিফ্রেশ করা হয়, যদি প্রযোজ্য হয়। DataStoreCredentialRefreshListener ব্যবহার করা নিশ্চিত করুন এবং Credential.Builder.addRefreshListener(CredentialRefreshListener) ব্যবহার করে শংসাপত্রের জন্য এটি সেট করুন।

Servlet অনুমোদন কোড প্রবাহ

এই লাইব্রেরিটি মৌলিক ব্যবহারের ক্ষেত্রে অনুমোদন কোড প্রবাহকে উল্লেখযোগ্যভাবে সহজ করার জন্য সার্লেট হেল্পার ক্লাস প্রদান করে। আপনি শুধু AbstractAuthorizationCodeServlet এবং AbstractAuthorizationCodeCallbackServlet ( google-oauth-client-servlet থেকে) এর কংক্রিট সাবক্লাসগুলি প্রদান করুন এবং সেগুলিকে আপনার web.xml ফাইলে যুক্ত করুন৷ মনে রাখবেন যে আপনাকে এখনও আপনার ওয়েব অ্যাপ্লিকেশনের জন্য ব্যবহারকারী লগইনের যত্ন নিতে হবে এবং একটি ব্যবহারকারী আইডি বের করতে হবে।

নমুনা কোড:

public class ServletSample extends AbstractAuthorizationCodeServlet {

  @Override
  protected void doGet(HttpServletRequest request, HttpServletResponse response)
      throws IOException {
    // do stuff
  }

  @Override
  protected String getRedirectUri(HttpServletRequest req) throws ServletException, IOException {
    GenericUrl url = new GenericUrl(req.getRequestURL().toString());
    url.setRawPath("/oauth2callback");
    return url.build();
  }

  @Override
  protected AuthorizationCodeFlow initializeFlow() throws IOException {
    return new AuthorizationCodeFlow.Builder(BearerToken.authorizationHeaderAccessMethod(),
        new NetHttpTransport(),
        new JacksonFactory(),
        new GenericUrl("https://server.example.com/token"),
        new BasicAuthentication("s6BhdRkqt3", "7Fjfp0ZBr1KtDRbnfVdmIw"),
        "s6BhdRkqt3",
        "https://server.example.com/authorize").setCredentialDataStore(
            StoredCredential.getDefaultDataStore(
                new FileDataStoreFactory(new File("datastoredir"))))
        .build();
  }

  @Override
  protected String getUserId(HttpServletRequest req) throws ServletException, IOException {
    // return user ID
  }
}

public class ServletCallbackSample extends AbstractAuthorizationCodeCallbackServlet {

  @Override
  protected void onSuccess(HttpServletRequest req, HttpServletResponse resp, Credential credential)
      throws ServletException, IOException {
    resp.sendRedirect("/");
  }

  @Override
  protected void onError(
      HttpServletRequest req, HttpServletResponse resp, AuthorizationCodeResponseUrl errorResponse)
      throws ServletException, IOException {
    // handle error
  }

  @Override
  protected String getRedirectUri(HttpServletRequest req) throws ServletException, IOException {
    GenericUrl url = new GenericUrl(req.getRequestURL().toString());
    url.setRawPath("/oauth2callback");
    return url.build();
  }

  @Override
  protected AuthorizationCodeFlow initializeFlow() throws IOException {
    return new AuthorizationCodeFlow.Builder(BearerToken.authorizationHeaderAccessMethod(),
        new NetHttpTransport(),
        new JacksonFactory(),
        new GenericUrl("https://server.example.com/token"),
        new BasicAuthentication("s6BhdRkqt3", "7Fjfp0ZBr1KtDRbnfVdmIw"),
        "s6BhdRkqt3",
        "https://server.example.com/authorize").setCredentialDataStore(
            StoredCredential.getDefaultDataStore(
                new FileDataStoreFactory(new File("datastoredir"))))
        .build();
  }

  @Override
  protected String getUserId(HttpServletRequest req) throws ServletException, IOException {
    // return user ID
  }
}

গুগল অ্যাপ ইঞ্জিন অনুমোদন কোড প্রবাহ

অ্যাপ ইঞ্জিনে অনুমোদন কোড ফ্লো প্রায় সার্লেট অনুমোদন কোড প্রবাহের অনুরূপ, তা ছাড়া আমরা Google অ্যাপ ইঞ্জিনের ব্যবহারকারীদের Java API ব্যবহার করতে পারি। ব্যবহারকারীদের Java API সক্রিয় করার জন্য ব্যবহারকারীকে লগ ইন করতে হবে; ব্যবহারকারীরা যদি ইতিমধ্যেই লগইন না করে থাকেন তাহলে একটি লগইন পৃষ্ঠায় পুনঃনির্দেশিত করার বিষয়ে তথ্যের জন্য, নিরাপত্তা এবং প্রমাণীকরণ (web.xml-এ) দেখুন।

সার্লেট কেস থেকে প্রাথমিক পার্থক্য হল যে আপনি AbstractAppEngineAuthorizationCodeServlet এবং AbstractAppEngineAuthorizationCodeCallbackServlet ( google-oauth-client-appengine থেকে) এর কংক্রিট সাবক্লাস প্রদান করেন। তারা অ্যাবস্ট্রাক্ট সার্লেট ক্লাস প্রসারিত করে এবং Users Java API ব্যবহার করে আপনার জন্য getUserId পদ্ধতি প্রয়োগ করে। AppEngineDataStoreFactory ( জাভার জন্য Google HTTP ক্লায়েন্ট লাইব্রেরি থেকে Google অ্যাপ ইঞ্জিন ডেটা স্টোর API ব্যবহার করে শংসাপত্র বজায় রাখার জন্য একটি ভাল বিকল্প।

নমুনা কোড:

public class AppEngineSample extends AbstractAppEngineAuthorizationCodeServlet {

  @Override
  protected void doGet(HttpServletRequest request, HttpServletResponse response)
      throws IOException {
    // do stuff
  }

  @Override
  protected String getRedirectUri(HttpServletRequest req) throws ServletException, IOException {
    GenericUrl url = new GenericUrl(req.getRequestURL().toString());
    url.setRawPath("/oauth2callback");
    return url.build();
  }

  @Override
  protected AuthorizationCodeFlow initializeFlow() throws IOException {
    return new AuthorizationCodeFlow.Builder(BearerToken.authorizationHeaderAccessMethod(),
        new UrlFetchTransport(),
        new JacksonFactory(),
        new GenericUrl("https://server.example.com/token"),
        new BasicAuthentication("s6BhdRkqt3", "7Fjfp0ZBr1KtDRbnfVdmIw"),
        "s6BhdRkqt3",
        "https://server.example.com/authorize").setCredentialStore(
            StoredCredential.getDefaultDataStore(AppEngineDataStoreFactory.getDefaultInstance()))
        .build();
  }
}

public class AppEngineCallbackSample extends AbstractAppEngineAuthorizationCodeCallbackServlet {

  @Override
  protected void onSuccess(HttpServletRequest req, HttpServletResponse resp, Credential credential)
      throws ServletException, IOException {
    resp.sendRedirect("/");
  }

  @Override
  protected void onError(
      HttpServletRequest req, HttpServletResponse resp, AuthorizationCodeResponseUrl errorResponse)
      throws ServletException, IOException {
    // handle error
  }

  @Override
  protected String getRedirectUri(HttpServletRequest req) throws ServletException, IOException {
    GenericUrl url = new GenericUrl(req.getRequestURL().toString());
    url.setRawPath("/oauth2callback");
    return url.build();
  }

  @Override
  protected AuthorizationCodeFlow initializeFlow() throws IOException {
    return new AuthorizationCodeFlow.Builder(BearerToken.authorizationHeaderAccessMethod(),
        new UrlFetchTransport(),
        new JacksonFactory(),
        new GenericUrl("https://server.example.com/token"),
        new BasicAuthentication("s6BhdRkqt3", "7Fjfp0ZBr1KtDRbnfVdmIw"),
        "s6BhdRkqt3",
        "https://server.example.com/authorize").setCredentialStore(
            StoredCredential.getDefaultDataStore(AppEngineDataStoreFactory.getDefaultInstance()))
        .build();
  }
}

কমান্ড লাইন অনুমোদন কোড প্রবাহ

দৈনিকমোশন-cmdline-নমুনা থেকে নেওয়া সরলীকৃত উদাহরণ কোড:

/** Authorizes the installed application to access user's protected data. */
private static Credential authorize() throws Exception {
  OAuth2ClientCredentials.errorIfNotSpecified();
  // set up authorization code flow
  AuthorizationCodeFlow flow = new AuthorizationCodeFlow.Builder(BearerToken
      .authorizationHeaderAccessMethod(),
      HTTP_TRANSPORT,
      JSON_FACTORY,
      new GenericUrl(TOKEN_SERVER_URL),
      new ClientParametersAuthentication(
          OAuth2ClientCredentials.API_KEY, OAuth2ClientCredentials.API_SECRET),
      OAuth2ClientCredentials.API_KEY,
      AUTHORIZATION_SERVER_URL).setScopes(Arrays.asList(SCOPE))
      .setDataStoreFactory(DATA_STORE_FACTORY).build();
  // authorize
  LocalServerReceiver receiver = new LocalServerReceiver.Builder().setHost(
      OAuth2ClientCredentials.DOMAIN).setPort(OAuth2ClientCredentials.PORT).build();
  return new AuthorizationCodeInstalledApp(flow, receiver).authorize("user");
}

private static void run(HttpRequestFactory requestFactory) throws IOException {
  DailyMotionUrl url = new DailyMotionUrl("https://api.dailymotion.com/videos/favorites");
  url.setFields("id,tags,title,url");

  HttpRequest request = requestFactory.buildGetRequest(url);
  VideoFeed videoFeed = request.execute().parseAs(VideoFeed.class);
  ...
}

public static void main(String[] args) {
  ...
  DATA_STORE_FACTORY = new FileDataStoreFactory(DATA_STORE_DIR);
  final Credential credential = authorize();
  HttpRequestFactory requestFactory =
      HTTP_TRANSPORT.createRequestFactory(new HttpRequestInitializer() {
        @Override
        public void initialize(HttpRequest request) throws IOException {
          credential.initialize(request);
          request.setParser(new JsonObjectParser(JSON_FACTORY));
        }
      });
  run(requestFactory);
  ...
}

ব্রাউজার-ভিত্তিক ক্লায়েন্ট প্রবাহ

ইমপ্লিসিট গ্রান্ট স্পেসিফিকেশনে নির্দিষ্ট করা ব্রাউজার-ভিত্তিক ক্লায়েন্ট প্রবাহের সাধারণ ধাপগুলি হল:

  • BrowserClientRequestUrl ব্যবহার করে, শেষ ব্যবহারকারীর ব্রাউজারটিকে অনুমোদন পৃষ্ঠায় পুনঃনির্দেশ করুন যেখানে শেষ ব্যবহারকারী তাদের সুরক্ষিত ডেটাতে আপনার অ্যাপ্লিকেশন অ্যাক্সেস দিতে পারে।
  • অনুমোদন সার্ভারের সাথে নিবন্ধিত রিডাইরেক্ট ইউআরআই-এ URL খণ্ডে পাওয়া অ্যাক্সেস টোকেন প্রক্রিয়া করতে একটি JavaScript অ্যাপ্লিকেশন ব্যবহার করুন।

একটি ওয়েব অ্যাপ্লিকেশনের জন্য নমুনা ব্যবহার:

public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
  String url = new BrowserClientRequestUrl(
      "https://server.example.com/authorize", "s6BhdRkqt3").setState("xyz")
      .setRedirectUri("https://client.example.com/cb").build();
  response.sendRedirect(url);
}

মেয়াদোত্তীর্ণ অ্যাক্সেস টোকেন সনাক্ত করা হচ্ছে

OAuth 2.0 ধারক স্পেসিফিকেশন অনুসারে, যখন সার্ভারকে একটি মেয়াদোত্তীর্ণ অ্যাক্সেস টোকেন সহ একটি সুরক্ষিত সংস্থান অ্যাক্সেস করতে বলা হয়, তখন সার্ভার সাধারণত একটি HTTP 401 Unauthorized স্ট্যাটাস কোডের সাথে প্রতিক্রিয়া জানায় যেমন নিম্নলিখিত:

   HTTP/1.1 401 Unauthorized
   WWW-Authenticate: Bearer realm="example",
                     error="invalid_token",
                     error_description="The access token expired"

যাইহোক, স্পেসিফিকেশনে অনেক নমনীয়তা রয়েছে বলে মনে হচ্ছে। বিস্তারিত জানার জন্য, OAuth 2.0 প্রদানকারীর ডকুমেন্টেশন চেক করুন।

একটি বিকল্প পদ্ধতি হল অ্যাক্সেস টোকেন প্রতিক্রিয়াতে expires_in প্যারামিটার পরীক্ষা করা। এটি মঞ্জুর করা অ্যাক্সেস টোকেনের সেকেন্ডে জীবনকাল নির্দিষ্ট করে, যা সাধারণত এক ঘন্টা। যাইহোক, অ্যাক্সেস টোকেন আসলে সেই সময়ের শেষে মেয়াদ শেষ নাও হতে পারে, এবং সার্ভার অ্যাক্সেসের অনুমতি দেওয়া চালিয়ে যেতে পারে। এই কারণেই আমরা সাধারণত 401 Unauthorized স্ট্যাটাস কোডের জন্য অপেক্ষা করার পরামর্শ দিই, টোকেনটি অতিবাহিত সময়ের উপর ভিত্তি করে মেয়াদ শেষ হয়ে গেছে বলে ধরে না নিয়ে। বিকল্পভাবে, আপনি একটি অ্যাক্সেস টোকেন মেয়াদ শেষ হওয়ার কিছুক্ষণ আগে রিফ্রেশ করার চেষ্টা করতে পারেন এবং যদি টোকেন সার্ভারটি অনুপলব্ধ হয়, আপনি 401 না পাওয়া পর্যন্ত অ্যাক্সেস টোকেন ব্যবহার করা চালিয়ে যান। এটি শংসাপত্রে ডিফল্টরূপে ব্যবহৃত কৌশল।

আরেকটি বিকল্প হল প্রতিটি অনুরোধের আগে একটি নতুন অ্যাক্সেস টোকেন গ্রহণ করা, কিন্তু এর জন্য প্রতিবার টোকেন সার্ভারে একটি অতিরিক্ত HTTP অনুরোধের প্রয়োজন হয়, তাই গতি এবং নেটওয়ার্ক ব্যবহারের ক্ষেত্রে এটি সম্ভবত একটি খারাপ পছন্দ। আদর্শভাবে, নতুন অ্যাক্সেস টোকেনগুলির জন্য একটি অ্যাপ্লিকেশনের অনুরোধগুলিকে ন্যূনতম করার জন্য অ্যাক্সেস টোকেনটিকে সুরক্ষিত, স্থায়ী সঞ্চয়স্থানে সংরক্ষণ করুন৷ (কিন্তু ইনস্টল করা অ্যাপ্লিকেশনের জন্য, নিরাপদ স্টোরেজ একটি কঠিন সমস্যা।)

নোট করুন যে একটি অ্যাক্সেস টোকেন মেয়াদ শেষ হওয়া ছাড়া অন্য কারণে অবৈধ হয়ে যেতে পারে, উদাহরণস্বরূপ যদি ব্যবহারকারী স্পষ্টভাবে টোকেনটি প্রত্যাহার করে থাকে, তাই নিশ্চিত করুন যে আপনার ত্রুটি-হ্যান্ডলিং কোডটি শক্তিশালী। একবার আপনি সনাক্ত করেছেন যে একটি টোকেন আর বৈধ নয়, উদাহরণস্বরূপ যদি এটি মেয়াদ উত্তীর্ণ হয় বা প্রত্যাহার করা হয়, তাহলে আপনাকে অবশ্যই আপনার স্টোরেজ থেকে অ্যাক্সেস টোকেনটি সরিয়ে ফেলতে হবে। Android এ, উদাহরণস্বরূপ, আপনাকে AccountManager.invalidateAuthToken কল করতে হবে।